├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENCE.txt ├── README.md ├── pom.xml └── src ├── main ├── java │ └── nl │ │ └── rutgerkok │ │ └── blocklocker │ │ ├── AttackType.java │ │ ├── BlockLockerAPI.java │ │ ├── BlockLockerAPIv2.java │ │ ├── BlockLockerPlugin.java │ │ ├── ChestSettings.java │ │ ├── OpenBlockSound.java │ │ ├── Permissions.java │ │ ├── ProfileFactory.java │ │ ├── ProtectableBlocksSettings.java │ │ ├── ProtectionCache.java │ │ ├── ProtectionFinder.java │ │ ├── ProtectionSign.java │ │ ├── ProtectionType.java │ │ ├── ProtectionUpdater.java │ │ ├── SearchMode.java │ │ ├── SecretSignEntry.java │ │ ├── SignParser.java │ │ ├── SignType.java │ │ ├── Translator.java │ │ ├── event │ │ ├── PlayerProtectionCreateEvent.java │ │ └── package-info.java │ │ ├── group │ │ ├── CombinedGroupSystem.java │ │ ├── GroupSystem.java │ │ └── package-info.java │ │ ├── impl │ │ ├── BlockLockerPluginImpl.java │ │ ├── ChestSettingsImpl.java │ │ ├── CompleteDoor.java │ │ ├── Config.java │ │ ├── ConfigTranslator.java │ │ ├── HopperCacheImpl.java │ │ ├── JsonSecretSignEntry.java │ │ ├── NbtSecretSignEntry.java │ │ ├── ProtectionFinderImpl.java │ │ ├── ProtectionSignImpl.java │ │ ├── ProtectionUpdaterImpl.java │ │ ├── SchedulerSupport.java │ │ ├── SignParserImpl.java │ │ ├── blockfinder │ │ │ ├── BlockFinder.java │ │ │ ├── ConnectedContainersBlockFinder.java │ │ │ ├── SeparateContainersBlockFinder.java │ │ │ └── package-info.java │ │ ├── event │ │ │ ├── BlockDestroyListener.java │ │ │ ├── BlockLockerCommand.java │ │ │ ├── BlockPlaceListener.java │ │ │ ├── EventListener.java │ │ │ ├── GolemListener.java │ │ │ ├── InteractListener.java │ │ │ ├── SignChangeListener.java │ │ │ └── package-info.java │ │ ├── group │ │ │ ├── FactionsGroupSystem.java │ │ │ ├── GuildsGroupSystem.java │ │ │ ├── PermissionsGroupSystem.java │ │ │ ├── ScoreboardGroupSystem.java │ │ │ ├── SimpleClansGroupSystem.java │ │ │ ├── TownyGroupSystem.java │ │ │ ├── mcMMOGroupSystem.java │ │ │ └── package-info.java │ │ ├── location │ │ │ └── TownyLocationChecker.java │ │ ├── package-info.java │ │ ├── profile │ │ │ ├── EveryoneProfileImpl.java │ │ │ ├── GolemProfileImpl.java │ │ │ ├── GroupLeaderProfileImpl.java │ │ │ ├── GroupProfileImpl.java │ │ │ ├── PlayerProfileImpl.java │ │ │ ├── ProfileFactoryImpl.java │ │ │ ├── RedstoneProfileImpl.java │ │ │ ├── TimerProfileImpl.java │ │ │ └── package-info.java │ │ ├── protection │ │ │ ├── AbstractProtection.java │ │ │ ├── AttachedProtectionImpl.java │ │ │ ├── ContainerProtectionImpl.java │ │ │ ├── DoorProtectionImpl.java │ │ │ └── package-info.java │ │ └── updater │ │ │ ├── UpdateCheckResult.java │ │ │ ├── UpdateChecker.java │ │ │ ├── UpdateNotifier.java │ │ │ ├── UpdatePreference.java │ │ │ ├── UpdateResult.java │ │ │ ├── Updater.java │ │ │ ├── UserAgent.java │ │ │ └── package-info.java │ │ ├── location │ │ ├── CombinedLocationChecker.java │ │ ├── IllegalLocationException.java │ │ └── LocationChecker.java │ │ ├── profile │ │ ├── GroupProfile.java │ │ ├── PlayerProfile.java │ │ ├── Profile.java │ │ ├── TimerProfile.java │ │ └── package-info.java │ │ └── protection │ │ ├── AttachedProtection.java │ │ ├── ContainerProtection.java │ │ ├── DoorProtection.java │ │ ├── Protection.java │ │ └── package-info.java └── resources │ ├── config.yml │ ├── plugin.yml │ ├── translations-cs.yml │ ├── translations-de.yml │ ├── translations-en.yml │ ├── translations-es.yml │ ├── translations-fr.yml │ ├── translations-hu.yml │ ├── translations-id.yml │ ├── translations-it.yml │ ├── translations-ja.yml │ ├── translations-kr.yml │ ├── translations-nl.yml │ ├── translations-pl.yml │ ├── translations-tr.yml │ ├── translations-vi.yml │ ├── translations-zh-hk.yml │ ├── translations-zh-tw.yml │ └── translations-zh.yml └── test └── java └── nl └── rutgerkok └── blocklocker ├── TestPlayer.java ├── group └── CombinedGroupSystemTest.java └── impl └── profile ├── NullTranslator.java └── TestPlayerProfile.java /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: dev build 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | java: [21, 25] 13 | steps: 14 | - uses: actions/checkout@v5 15 | - name: Set up JDK 16 | uses: actions/setup-java@v4 17 | with: 18 | distribution: 'temurin' 19 | java-version: ${{ matrix.java }} 20 | cache: maven 21 | - name: Build with Maven 22 | run: mvn --batch-mode --update-snapshots verify 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Eclipse 2 | .project 3 | .classpath 4 | .settings/ 5 | 6 | # IntelliJ 7 | .idea/ 8 | *.iml 9 | 10 | # Maven 11 | target/ 12 | 13 | # Other 14 | *.bat 15 | logs/latest.log 16 | -------------------------------------------------------------------------------- /LICENCE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Rutger Kok 4 | Copyright (c) contributors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | nl.rutgerkok 6 | blocklocker 7 | 1.14.1-SNAPSHOT 8 | jar 9 | BlockLocker 10 | https://github.com/rutgerkok/BlockLocker 11 | 12 | UTF-8 13 | 5.3.1 14 | 21 15 | 16 | 17 | 18 | papermc 19 | https://repo.papermc.io/repository/maven-public/ 20 | 21 | 22 | rutger-repo 23 | https://rutgerkok.nl/repo 24 | 25 | 26 | codemc-repo 27 | https://repo.codemc.org/repository/maven-public/ 28 | 29 | 30 | 31 | 32 | io.papermc.paper 33 | paper-api 34 | 1.21.10-R0.1-SNAPSHOT 35 | provided 36 | 37 | 38 | 39 | com.palmergames 40 | towny 41 | 0.96.7.0 42 | provided 43 | 44 | 45 | com.massivecraft 46 | MassiveCore 47 | 2.7.5 48 | provided 49 | 50 | 51 | com.massivecraft 52 | Factions 53 | 2.7.5 54 | provided 55 | 56 | 57 | com.gmail.nossr50.mcMMO 58 | mcMMO 59 | 2.1.218 60 | provided 61 | 62 | 63 | me.glaremasters 64 | guilds 65 | 3.5.3.5-RELEASE 66 | provided 67 | 68 | 69 | net.sacredlabyrinth.phaed.simpleclans 70 | SimpleClans 71 | 2.15.1 72 | provided 73 | 74 | 75 | 76 | org.junit.jupiter 77 | junit-jupiter-api 78 | ${junit.jupiter.version} 79 | test 80 | 81 | 82 | org.junit.jupiter 83 | junit-jupiter-engine 84 | ${junit.jupiter.version} 85 | test 86 | 87 | 88 | org.mockito 89 | mockito-core 90 | 2.23.0 91 | test 92 | 93 | 94 | 95 | 97 | 98 | 99 | . 100 | true 101 | src/main/resources 102 | 103 | * 104 | 105 | 106 | 107 | 108 | 109 | 110 | org.apache.maven.plugins 111 | maven-jar-plugin 112 | 3.4.2 113 | 114 | 115 | true 116 | 117 | true 118 | 119 | 120 | mojang 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | codemc-releases 130 | https://repo.codemc.io/repository/maven-releases/ 131 | 132 | 133 | codemc-snapshots 134 | https://repo.codemc.io/repository/maven-snapshots/ 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/AttackType.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker; 2 | 3 | import nl.rutgerkok.blocklocker.protection.Protection; 4 | 5 | /** 6 | * Enumeration of things (excluding players) that can attack a 7 | * {@link Protection}. 8 | * 9 | */ 10 | public enum AttackType { 11 | 12 | CREEPER, 13 | ENDERMAN, 14 | BLOCK_EXPLOSION, 15 | FIRE, 16 | GOLEM, 17 | GHAST, 18 | PISTON, 19 | SAPLING, 20 | TNT, 21 | UNKNOWN, 22 | VILLAGER, 23 | ZOMBIE 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/BlockLockerAPI.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker; 2 | 3 | import org.bukkit.OfflinePlayer; 4 | import org.bukkit.block.Block; 5 | import org.bukkit.entity.Player; 6 | import org.bukkit.plugin.java.JavaPlugin; 7 | 8 | import com.google.common.base.Optional; 9 | 10 | /** 11 | * This class is intended as an easy way for other plugin developers to hook 12 | * into this plugin. Binary compatibility for all methods in this class will be 13 | * kept for all future releases of the plugin, unless stated otherwise. In other 14 | * words: if your plugin uses only the methods in this class, it won't break for 15 | * any future releases of this plugin. 16 | * 17 | * @deprecated This class uses google guava's Optional.class, a new Version was made that utilises java.util.Optional. 18 | * @see BlockLockerAPIv2 19 | * 20 | */ 21 | @Deprecated 22 | public final class BlockLockerAPI { 23 | 24 | /** 25 | * Gets the owner of the given block. 26 | * 27 | * @param block 28 | * The block. 29 | * @return The owner, or empty if the block is not protected. 30 | */ 31 | public static Optional getOwner(Block block) { 32 | return Optional.fromJavaUtil(BlockLockerAPIv2.getOwner(block)); 33 | } 34 | 35 | /** 36 | * Gets the display name of the owner of the block. 37 | * 38 | * @param block 39 | * The block. 40 | * @return The display name, or {@code Optional.absent()} if the block isn't 41 | * protected. Unlike {@link #getOwner(Block)}, this method still 42 | * returns the name of the owner even if the UUID of the owner is 43 | * not yet known. 44 | */ 45 | public static Optional getOwnerDisplayName(Block block) { 46 | return Optional.fromJavaUtil(BlockLockerAPIv2.getOwnerDisplayName(block)); 47 | } 48 | 49 | /** 50 | * Gets access to the plugin instance, allowing for much more control of the 51 | * plugin. Binary compatibility with future versions of the plugin 52 | * is not guaranteed. 53 | * 54 | * @return The plugin instance. 55 | */ 56 | public static final BlockLockerPlugin getPlugin() { 57 | return (BlockLockerPlugin) JavaPlugin.getProvidingPlugin(BlockLockerAPI.class); 58 | } 59 | 60 | /** 61 | * Checks if the player is allowed in the protection. 62 | * 63 | * @param player 64 | * The player to check. 65 | * @param block 66 | * The block. 67 | * @param allowBypass 68 | * If the player has the bypass permission, or if the protection 69 | * is expired, the plugin will return true regardless of whether 70 | * the player is listed on the signs. 71 | * @return True if 72 | * 78 | * False otherwise. 79 | */ 80 | public static boolean isAllowed(Player player, Block block, boolean allowBypass) { 81 | return BlockLockerAPIv2.isAllowed(player, block, allowBypass); 82 | } 83 | 84 | /** 85 | * Gets whether the player is the owner of the protection. Only owners are 86 | * allowed to destroy the protection. 87 | * 88 | * @param player 89 | * The player. 90 | * @param block 91 | * The block. 92 | * @return True if the player is the owner, false if the player is not the 93 | * owner or if the block is unprotected. 94 | */ 95 | public static boolean isOwner(Player player, Block block) { 96 | return BlockLockerAPIv2.isOwner(player, block); 97 | } 98 | 99 | /** 100 | * Checks if the given block is protected by the plugin. 101 | * 102 | * @param block 103 | * The block to check. 104 | * @return True if the given block is protected, false otherwise. 105 | */ 106 | public static boolean isProtected(Block block) { 107 | return BlockLockerAPIv2.isProtected(block); 108 | } 109 | 110 | private BlockLockerAPI() { 111 | // No instances 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/BlockLockerPlugin.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker; 2 | 3 | import java.util.logging.Logger; 4 | 5 | import org.bukkit.block.Block; 6 | import org.bukkit.event.Event; 7 | 8 | import nl.rutgerkok.blocklocker.group.CombinedGroupSystem; 9 | import nl.rutgerkok.blocklocker.group.GroupSystem; 10 | import nl.rutgerkok.blocklocker.location.CombinedLocationChecker; 11 | 12 | /** 13 | * Main entry point of the plugin. 14 | * 15 | */ 16 | public interface BlockLockerPlugin { 17 | 18 | /** 19 | * Calls the given event, allowing other plugins to react to this event. 20 | * 21 | * @param The type of the event. 22 | * @param event The event, may not be null. 23 | * @return The same event, for chaining. 24 | */ 25 | E callEvent(E event); 26 | 27 | /** 28 | * Gets the {@link ChestSettings} object, containing all customizable 29 | * aspects of the plugin. 30 | * 31 | * @return The settings object. 32 | */ 33 | ChestSettings getChestSettings(); 34 | 35 | /** 36 | * Gets the combined group system of the plugin, which can be used to add 37 | * other group systems. 38 | * 39 | * @return The combined group system. 40 | * @see CombinedGroupSystem#addSystem(GroupSystem) 41 | */ 42 | CombinedGroupSystem getGroupSystems(); 43 | 44 | /** 45 | * Gets the cache that stores whether a block is protected or not. 46 | * @return The cache. 47 | */ 48 | ProtectionCache getProtectionCache(); 49 | 50 | /** 51 | * Gets the location checkers, which are used to prevent players from placing chests in the wilderness. 52 | * @return A location checker. 53 | */ 54 | CombinedLocationChecker getLocationCheckers(); 55 | 56 | /** 57 | * Gets the logger of the plugin. 58 | * 59 | * @return The logger. 60 | */ 61 | Logger getLogger(); 62 | 63 | /** 64 | * Gets the profile factory, used to create profiles. 65 | * 66 | * @return The profile factory. 67 | */ 68 | ProfileFactory getProfileFactory(); 69 | 70 | /** 71 | * Gets the protection finder, used to find protections in the world. 72 | * 73 | * @return The protection finder. 74 | */ 75 | ProtectionFinder getProtectionFinder(); 76 | 77 | /** 78 | * Gets the protection updater, used to mark protections as needing an 79 | * update, for example for fixing missing UUIDs. 80 | * 81 | * @return The protection updater. 82 | */ 83 | ProtectionUpdater getProtectionUpdater(); 84 | 85 | /** 86 | * Gets the {@link SignParser} object. 87 | * 88 | * @return The sign parser. 89 | */ 90 | SignParser getSignParser(); 91 | 92 | /** 93 | * Gets the translator, used to translate messages. 94 | * 95 | * @return The translator. 96 | */ 97 | Translator getTranslator(); 98 | 99 | /** 100 | * Reloads the configuration files of the plugin. 101 | */ 102 | void reload(); 103 | 104 | /** 105 | * Runs a task the next tick on the server thread. 106 | * 107 | * @param block 108 | * Used to run the task in the region of the block (when using the 109 | * Folia server). 110 | * @param runnable 111 | * The task. 112 | */ 113 | void runLater(Block block, Runnable runnable); 114 | 115 | /** 116 | * Runs a task in the given amount of ticks on the server thread. 117 | * 118 | * @param block 119 | * Used to run the task in the region of the block (when using the 120 | * Folia server). 121 | * @param runnable 122 | * The task. 123 | * @param ticks 124 | * In how many ticks the method needs to run. 125 | */ 126 | void runLater(Block block, Runnable runnable, int ticks); 127 | 128 | /** 129 | * Runs a task on the server thread. On Folia, this thread only controls things 130 | * that don't belong to a specific region, like weather and time. 131 | * 132 | * @param runnable 133 | * The task. 134 | * @param ticks 135 | * In how many ticks the method needs to run. 136 | */ 137 | void runLaterGlobally(Runnable runnable, int ticks); 138 | 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/ChestSettings.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker; 2 | 3 | import java.util.Date; 4 | import java.util.List; 5 | import java.util.Optional; 6 | 7 | import org.bukkit.block.Block; 8 | 9 | /** 10 | * Represents all settings of the plugin. 11 | * 12 | */ 13 | public interface ChestSettings extends ProtectableBlocksSettings { 14 | 15 | /** 16 | * Gets whether the given attack type is allowed to destroy protections. 17 | * 18 | * @param attackType 19 | * The attack type. 20 | * @return True if the attack type is allowed to destroy protections, false 21 | * otherwise. 22 | */ 23 | boolean allowDestroyBy(AttackType attackType); 24 | 25 | /** 26 | * Returns whether containers of the same type require only one sign. 27 | * @return True if containers are connected, false otherwise. 28 | */ 29 | boolean getConnectContainers(); 30 | 31 | /** 32 | * Gets the actual date that chests must have activity after. If a chest 33 | * doesn't have activity after this date, it is considered expired. 34 | * 35 | * @return The date, or absent if chests never expire. 36 | */ 37 | Optional getChestExpireDate(); 38 | 39 | /** 40 | * Gets the default amount of ticks a door stays open before closing 41 | * automatically. When set to less than 1, the door is never closed 42 | * automatically. Players can override this value for a door. 43 | * 44 | * @return The amount. 45 | */ 46 | int getDefaultDoorOpenSeconds(); 47 | 48 | /** 49 | * Gets a mutable list of objects that specify additional protectables. You can 50 | * add your own instances here (that implement 51 | * {@link ProtectableBlocksSettings}). 52 | * 53 | * @return The mutable list. 54 | */ 55 | List getExtraProtectables(); 56 | 57 | /** 58 | * Gets the localized header for the given sign type, includes brackets and 59 | * colors. 60 | * 61 | * @param signType 62 | * The type of the sign. 63 | * @param header 64 | * The first line of the sign, to see which header to return. 65 | * @return The header. 66 | */ 67 | String getFancyLocalizedHeader(SignType signType, String header); 68 | 69 | /** 70 | * Gets the type of the protection. 71 | * 72 | * @param block 73 | * The main protection block. 74 | * @return Type of the protection. 75 | */ 76 | Optional getProtectionType(Block block); 77 | 78 | /** 79 | * Gets the localized headers for the given sign type, without colors. 80 | * 81 | * @param signType 82 | * The type of the sign. 83 | * @return All possible headers. 84 | */ 85 | List getSimpleLocalizedHeaders(SignType signType); 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/OpenBlockSound.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker; 2 | 3 | import org.bukkit.Material; 4 | import org.bukkit.Sound; 5 | 6 | /** 7 | * Sounds for when a block is opened/closed. 8 | * 9 | */ 10 | public final class OpenBlockSound { 11 | 12 | /** 13 | * Gets the sound for opening/closing the given material. For unknown 14 | * materials, a generic sound is returned. 15 | * 16 | * @param material 17 | * The material. 18 | * @param open 19 | * Whether ther material is opened. 20 | * @return The sound. 21 | */ 22 | public static Sound get(Material material, boolean open) { 23 | if (open) { 24 | switch (material) { 25 | case ACACIA_FENCE_GATE: 26 | case BIRCH_FENCE_GATE: 27 | case DARK_OAK_FENCE_GATE: 28 | case JUNGLE_FENCE_GATE: 29 | case SPRUCE_FENCE_GATE: 30 | case OAK_FENCE_GATE: 31 | return Sound.BLOCK_FENCE_GATE_OPEN; 32 | case OAK_DOOR: 33 | case SPRUCE_DOOR: 34 | case BIRCH_DOOR: 35 | case JUNGLE_DOOR: 36 | case ACACIA_DOOR: 37 | case DARK_OAK_DOOR: 38 | return Sound.BLOCK_WOODEN_DOOR_OPEN; 39 | case IRON_DOOR: 40 | return Sound.BLOCK_IRON_DOOR_OPEN; 41 | case OAK_TRAPDOOR: 42 | case SPRUCE_TRAPDOOR: 43 | case BIRCH_TRAPDOOR: 44 | case JUNGLE_TRAPDOOR: 45 | case ACACIA_TRAPDOOR: 46 | case DARK_OAK_TRAPDOOR: 47 | return Sound.BLOCK_WOODEN_TRAPDOOR_OPEN; 48 | case IRON_TRAPDOOR: 49 | return Sound.BLOCK_IRON_TRAPDOOR_OPEN; 50 | default: 51 | return Sound.BLOCK_WOODEN_TRAPDOOR_OPEN; 52 | } 53 | } else { 54 | switch (material) { 55 | case ACACIA_FENCE_GATE: 56 | case BIRCH_FENCE_GATE: 57 | case DARK_OAK_FENCE_GATE: 58 | case JUNGLE_FENCE_GATE: 59 | case SPRUCE_FENCE_GATE: 60 | case OAK_FENCE_GATE: 61 | return Sound.BLOCK_FENCE_GATE_CLOSE; 62 | case OAK_DOOR: 63 | case SPRUCE_DOOR: 64 | case BIRCH_DOOR: 65 | case JUNGLE_DOOR: 66 | case ACACIA_DOOR: 67 | case DARK_OAK_DOOR: 68 | return Sound.BLOCK_WOODEN_DOOR_CLOSE; 69 | case IRON_DOOR: 70 | return Sound.BLOCK_IRON_DOOR_CLOSE; 71 | case OAK_TRAPDOOR: 72 | case SPRUCE_TRAPDOOR: 73 | case BIRCH_TRAPDOOR: 74 | case JUNGLE_TRAPDOOR: 75 | case ACACIA_TRAPDOOR: 76 | case DARK_OAK_TRAPDOOR: 77 | return Sound.BLOCK_WOODEN_TRAPDOOR_CLOSE; 78 | case IRON_TRAPDOOR: 79 | return Sound.BLOCK_IRON_TRAPDOOR_CLOSE; 80 | default: 81 | return Sound.BLOCK_WOODEN_TRAPDOOR_CLOSE; 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/Permissions.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker; 2 | 3 | import java.util.Locale; 4 | 5 | import org.bukkit.permissions.Permission; 6 | import org.bukkit.permissions.PermissionDefault; 7 | 8 | public final class Permissions { 9 | public static final String CAN_BYPASS = "blocklocker.bypass"; 10 | public static final String CAN_ADMIN = "blocklocker.admin"; 11 | public static final String CAN_PROTECT = "blocklocker.protect"; 12 | public static final String CAN_RELOAD = "blocklocker.reload"; 13 | public static final String CAN_WILDERNESS = "blocklocker.wilderness"; 14 | 15 | private static final String GROUP_PREFIX = "blocklocker.group."; 16 | 17 | /** 18 | * Gets the permission node that a player needs to have to be considered 19 | * part of a group. 20 | * 21 | * @param groupName 22 | * The name of the group. 23 | * @return The permission node. 24 | */ 25 | public static Permission getGroupNode(String groupName) { 26 | return new Permission(GROUP_PREFIX + groupName.toLowerCase(Locale.ROOT), PermissionDefault.FALSE); 27 | } 28 | 29 | private Permissions() { 30 | // No instances! 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/ProfileFactory.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker; 2 | 3 | import java.util.Optional; 4 | import java.util.UUID; 5 | 6 | import org.bukkit.entity.Player; 7 | 8 | import nl.rutgerkok.blocklocker.profile.PlayerProfile; 9 | import nl.rutgerkok.blocklocker.profile.Profile; 10 | 11 | public interface ProfileFactory { 12 | 13 | /** 14 | * Gets the profile represented by [Everyone] on a sign. Allows everyone 15 | * else to access the sign. 16 | * 17 | * @return The profile. 18 | */ 19 | Profile fromEveryone(); 20 | 21 | /** 22 | * Creates a new profile from the given name and id. 23 | * 24 | * @param name 25 | * The name. 26 | * @param uuid 27 | * The uuid. When absent, comparisons between other 28 | * {@link PlayerProfile} the plugin may try to fetch this UUID at 29 | * any time based on the name. {@code new UUID(0,0)} 30 | * @return The profile. 31 | */ 32 | PlayerProfile fromNameAndUniqueId(String name, Optional uuid); 33 | 34 | /** 35 | * Creates a new profile representing the online player. 36 | * 37 | * @param player 38 | * The online player. 39 | * @return The profile. 40 | */ 41 | PlayerProfile fromPlayer(Player player); 42 | 43 | /** 44 | * Gets the profile for redstone. 45 | * 46 | * @return The profile. 47 | */ 48 | Profile fromRedstone(); 49 | 50 | /** 51 | * Gets the profile for golems. 52 | * 53 | * @return The profile. 54 | */ 55 | Profile fromGolem(); 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/ProtectableBlocksSettings.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker; 2 | 3 | import org.bukkit.block.Block; 4 | 5 | import nl.rutgerkok.blocklocker.location.LocationChecker; 6 | 7 | /** 8 | * Used to determine what blocks can be protected. 9 | * 10 | * @see LocationChecker For blocks that can be protected, but not anywhere, for 11 | * example because of a claim. 12 | */ 13 | public interface ProtectableBlocksSettings { 14 | 15 | /** 16 | * Gets whether the block can be protected by any protection type (container, 17 | * door, etc.) 18 | * 19 | * @param block 20 | * Block to check. Note: if the block can be protected not because of 21 | * it's type/structure, but because of where it is located, it is 22 | * better to check this using a {@link LocationChecker} instead. 23 | * @return True if the block can be protected by the given type, false 24 | * otherwise. 25 | */ 26 | boolean canProtect(Block block); 27 | 28 | /** 29 | * Gets whether the material can be protected by the given protection type. 30 | * 31 | * @param type 32 | * Protection type that must be checked for being able to protect 33 | * this type. Note: if the block can be protected not because of it's 34 | * type/structure, but because of where it is located, it is better 35 | * to check this using a {@link LocationChecker} instead. 36 | * @param block 37 | * Block to check. 38 | * 39 | * @return True if the block can be protected by the given type, false 40 | * otherwise. 41 | */ 42 | boolean canProtect(ProtectionType type, Block block); 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/ProtectionCache.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker; 2 | 3 | import org.bukkit.block.Block; 4 | 5 | /** 6 | * A simple cache that stores whether a block is protected. Entries expire after 7 | * a few seconds. Used for hoppers and copper golems, which do a lot of successive 8 | * checks. 9 | */ 10 | public interface ProtectionCache { 11 | 12 | enum CacheFlag { 13 | /** 14 | * Block is certainly protected. 15 | */ 16 | NOT_ALLOWED, 17 | /** 18 | * Block is not protected. 19 | */ 20 | ALLOWED, 21 | /** 22 | * Not cached; we don't know whether the block is protected or not. 23 | */ 24 | MISS_CACHE 25 | } 26 | 27 | enum CacheType { 28 | /** 29 | * Checks whether redstone is allowed. 30 | */ 31 | REDSTONE, 32 | /** 33 | * Checks whether golems are allowed. 34 | */ 35 | GOLEM 36 | } 37 | 38 | /** 39 | * Gets whether redstone/golems are allowed at the given block. 40 | * 41 | * @param cacheType Golems or redstone. 42 | * @param block The block. 43 | * @return Whether the given block is locked. 44 | */ 45 | CacheFlag getAllowed(Block block, CacheType cacheType); 46 | 47 | /** 48 | * Sets whether the given block is locked for redstone. 49 | * 50 | * @param cacheType Golems or redstone. 51 | * @param block The block. 52 | * @param allowed True if redstone/golems are allowed. Always true if there's no protection at the location. 53 | */ 54 | void setAllowed(Block block, CacheType cacheType, boolean allowed); 55 | 56 | 57 | } -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/ProtectionFinder.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker; 2 | 3 | import java.util.Optional; 4 | 5 | import org.bukkit.block.Block; 6 | import org.bukkit.block.Sign; 7 | 8 | import nl.rutgerkok.blocklocker.profile.Profile; 9 | import nl.rutgerkok.blocklocker.protection.Protection; 10 | 11 | /** 12 | * Finds a {@link Protection} in the world. 13 | * 14 | */ 15 | public interface ProtectionFinder { 16 | 17 | /** 18 | * Gets the protection the sign is placed against. The contents of the sign 19 | * is not checked; in other words: the sign doesn't have to be part of the 20 | * protection (yet). 21 | * 22 | * @param signBlock 23 | * The sign. 24 | * @return The protection, or empty if the block is not a sign or not placed 25 | * against a protection. 26 | */ 27 | Optional findExistingProtectionForNewSign(Block signBlock); 28 | 29 | /** 30 | * Gets the protection the given block is part of. If the block is a sign, 31 | * the sign must already be filled in. 32 | * 33 | * @param block 34 | * The block to search at. 35 | * @return The protection, if any. 36 | */ 37 | Optional findProtection(Block block); 38 | 39 | /** 40 | * Gets the protection the given block is part of. If the block is a sign, 41 | * the sign must already be filled in. 42 | * 43 | * @param block 44 | * The block to search at. 45 | * @param searchMode 46 | * The search mode. 47 | * @return The protection, if any. 48 | */ 49 | Optional findProtection(Block block, SearchMode searchMode); 50 | 51 | /** 52 | * Gets whether this block can be protected by a sign. This can either be 53 | * because it is itself a block that can be locked (like a chest) or because it 54 | * supports a block that is protectable (like the block below a door). Note that 55 | * this method returns false for signs, since you cannot protect a sign with 56 | * another sign. See {@link #isSignNearbyProtectable(Block)} for that case. 57 | * 58 | * @param block 59 | * The block to check. 60 | * @return True if the block is protectable, false otherwise. 61 | */ 62 | boolean isProtectable(Block block); 63 | 64 | /** 65 | * Gets whether the given sign is near a block that can be protected. In 66 | * other words, this method returns true if the sign is attached to a chest, 67 | * furnace etc. or placed above a door. 68 | * 69 | *

70 | * This method doesn't care whether the block that can be protected is 71 | * actually protected. 72 | * 73 | * @param signBlock 74 | * The sign block. 75 | * @return True if the sign is placed near a block that can be protected. 76 | */ 77 | boolean isSignNearbyProtectable(Block signBlock); 78 | 79 | /** 80 | * Creates a new protection sign, ignoring the content already on the sign. 81 | * 82 | *

83 | * To inspect the contents of existing signs, use 84 | * {@link #findProtection(Block)}. 85 | * 86 | * @param sign 87 | * The sign. 88 | * @param signType 89 | * Type to set the sign to. 90 | * @param onFirstLine 91 | * Person to place on the first line. 92 | * @return The new protection sign. 93 | */ 94 | ProtectionSign newProtectionSign(Sign sign, SignType signType, Profile onFirstLine); 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/ProtectionSign.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker; 2 | 3 | import java.util.List; 4 | 5 | import org.bukkit.Location; 6 | 7 | import nl.rutgerkok.blocklocker.profile.Profile; 8 | 9 | /** 10 | * Represents the information on a protection sign in the world. Instances of 11 | * this interface must be immutable. 12 | * 13 | *

14 | * Two protection signs are considered equal when they are placed on the same 15 | * block in the same world and are of the same implementation. 16 | */ 17 | public interface ProtectionSign { 18 | 19 | /** 20 | * Gets the location of this sign. 21 | * 22 | * @return The location. 23 | */ 24 | Location getLocation(); 25 | 26 | /** 27 | * Gets all profiles currently on the sign. The list will have one, two or 28 | * three profiles. In other words: it is always save to call 29 | * {@code getProfiles().get(0)}. 30 | * 31 | * @return All profiles. 32 | */ 33 | List getProfiles(); 34 | 35 | /** 36 | * Gets the type of this sign. The type of the sign depends on the header of 37 | * the sign. 38 | * 39 | * @return The type. 40 | */ 41 | SignType getType(); 42 | 43 | /** 44 | * Returns true if this sign is saved in an outdated way (plain text, missing 45 | * header color/wrong casing) and would require a resave. 46 | * 47 | * @return Whether the sign requires a resaves. 48 | */ 49 | boolean requiresResave(); 50 | 51 | /** 52 | * Creates a new protection sign object with the given profiles. Existing 53 | * profiles are erased. If this sign is not of the type 54 | * {@link SignType#MORE_USERS} the first entry of the list will become the 55 | * owner. 56 | * 57 | *

58 | * This object is immutable and will not be modified. The actual sign in the 59 | * world will not be modified too. You must save the returned 60 | * {@link ProtectionSign} using {@link SignParser#saveSign(ProtectionSign)}. 61 | * 62 | * 63 | * @param profiles 64 | * The profiles for the protection sign. 65 | * @return The new object. * @throws NullPointerException If any entry in the 66 | * list is 0. 67 | * @throws IllegalArgumentException 68 | * If the list is empty, or if the list has a size larger than 3. 69 | */ 70 | ProtectionSign withProfiles(List profiles); 71 | 72 | /** 73 | * Returns a instance that requires a resave. 74 | * 75 | * @see #requiresResave() 76 | * @return An instance. 77 | */ 78 | ProtectionSign withRequiringResave(); 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/ProtectionType.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker; 2 | 3 | import nl.rutgerkok.blocklocker.protection.AttachedProtection; 4 | import nl.rutgerkok.blocklocker.protection.ContainerProtection; 5 | import nl.rutgerkok.blocklocker.protection.DoorProtection; 6 | 7 | /** 8 | * The different types of protections. 9 | * 10 | */ 11 | public enum ProtectionType { 12 | /** 13 | * A container, represented by {@link ContainerProtection}. 14 | */ 15 | CONTAINER, 16 | /** 17 | * A door, represented by {@link DoorProtection}. 18 | */ 19 | DOOR, 20 | /** 21 | * A block where signs can also be attached to the block it is 22 | * hanging/standing on. Represented {@link AttachedProtection}. 23 | */ 24 | ATTACHABLE; 25 | } -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/ProtectionUpdater.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker; 2 | 3 | import nl.rutgerkok.blocklocker.protection.Protection; 4 | 5 | /** 6 | * Queues protections for updates of the attached signs. In offline mode, just 7 | * the colors have to be updated, but in online mode the UUID may be looked up, 8 | * or the player name may be updated. 9 | * 10 | */ 11 | public interface ProtectionUpdater { 12 | 13 | /** 14 | * Does generic cleaning for the signs on the protection at some point in the 15 | * near future. Fixes the missing UUIDs in the given protection, by looking them 16 | * up if the player is online. 17 | * 18 | * @param protection 19 | * The protection to fix. If the protection was already queued for an 20 | * update, nothing happens. 21 | * @param newProtection 22 | * True if this protection is new, false otherwise. Currently has no 23 | * effect. 24 | */ 25 | void update(Protection protection, boolean newProtection); 26 | 27 | } -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/SearchMode.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker; 2 | 3 | /** 4 | * The types of blocks to look out for. 5 | * 6 | * A protection consists of three block types: 7 | *

    8 | *
  • Protection blocks, like door or chest blocks.
  • 9 | *
  • Blocks supporting the protection, can be of any material. For example, 10 | * the block under the door is a supporting block.
  • 11 | *
  • Signs.
  • 12 | *
13 | */ 14 | public enum SearchMode { 15 | /** 16 | * Ignores both {@link #NO_SIGNS signs} and {@link #NO_SUPPORTING_BLOCKS supporting blocks}. 17 | */ 18 | MAIN_BLOCKS_ONLY, 19 | /** 20 | * Ignores blocks that are solely part of the protection because they are 21 | * supporting another block. 22 | */ 23 | NO_SUPPORTING_BLOCKS, 24 | /** 25 | * Ignores protection signs. 26 | */ 27 | NO_SIGNS, 28 | /** 29 | * Includes all blocks (protection blocks, supporting blocks and signs) in 30 | * the search. 31 | */ 32 | ALL; 33 | 34 | /** 35 | * Gets whether supporting blocks should be searched for. If the block being 36 | * searched is a supporting block, the protection won't be found if this 37 | * method returns false. 38 | * 39 | * @return True if supporting blocks should be searched for, false 40 | * otherwise. 41 | */ 42 | public boolean searchForSupportingBlocks() { 43 | return this != NO_SUPPORTING_BLOCKS && this != MAIN_BLOCKS_ONLY; 44 | } 45 | 46 | /** 47 | * Gets whether signs should be searched for. If the block being searched is 48 | * a sign, the protection won't be found if this method returns false. 49 | * 50 | * @return True if supporting blocks should be searched for, false 51 | * otherwise. 52 | */ 53 | public boolean searchForSigns() { 54 | return this != NO_SIGNS && this != MAIN_BLOCKS_ONLY; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/SecretSignEntry.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker; 2 | 3 | import java.util.Optional; 4 | import java.util.OptionalInt; 5 | import java.util.UUID; 6 | 7 | /** 8 | * Used to store secret extra data on a sign. This class represents a single 9 | * entry, so it can for example store one player. 10 | * 11 | */ 12 | public interface SecretSignEntry { 13 | 14 | /** 15 | * Gets a boolean with the given name. 16 | * 17 | * @param key 18 | * The key, must only contain [a-z_]. 19 | * @return The value, if present. 20 | */ 21 | Optional getBoolean(String key); 22 | 23 | /** 24 | * Gets an integer with the given name. 25 | * 26 | * @param key 27 | * The key, must only contain [a-z_]. 28 | * @return The value, if present. 29 | */ 30 | OptionalInt getInteger(String key); 31 | 32 | /** 33 | * Gets a string with the given name. 34 | * 35 | * @param key 36 | * The key, must only contain [a-z_]. 37 | * @return The value, if present. 38 | */ 39 | Optional getString(String key); 40 | 41 | /** 42 | * Gets a unique id with the given name. 43 | * 44 | * @param key 45 | * The key, must only contain [a-z_]. 46 | * @return The value, if present. 47 | */ 48 | Optional getUniqueId(String key); 49 | 50 | /** 51 | * Sets a boolean with the given name. 52 | * 53 | * @param key 54 | * The key, must only contain [a-z_]. 55 | * @param value 56 | * The value. 57 | */ 58 | void setBoolean(String key, boolean value); 59 | 60 | /** 61 | * Sets an integer with the given name. 62 | * 63 | * @param key 64 | * The key, must only contain [a-z_]. 65 | * @param value 66 | * The value. 67 | */ 68 | void setInteger(String key, int value); 69 | 70 | /** 71 | * Sets a string with the given name. 72 | * 73 | * @param key 74 | * The key, must only contain [a-z_]. 75 | * @param value 76 | * The value, may not be null. 77 | */ 78 | void setString(String key, String value); 79 | 80 | /** 81 | * Sets a unique id with the given name. 82 | * 83 | * @param key 84 | * The key, must only contain [a-z_]. 85 | * @param value 86 | * The value, may not be null. 87 | */ 88 | void setUniqueId(String key, UUID value); 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/SignParser.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker; 2 | 3 | import java.util.Optional; 4 | 5 | import org.bukkit.block.Block; 6 | import org.bukkit.block.Sign; 7 | import org.bukkit.block.sign.Side; 8 | import org.bukkit.event.block.SignChangeEvent; 9 | 10 | /** 11 | * Parses a single sign. It essentially converts between {@link Sign} and 12 | * {@link ProtectionSign}. 13 | * 14 | */ 15 | public interface SignParser { 16 | 17 | /** 18 | * Gets the text-only display lines that should be displayed on the sign at the 19 | * front. 20 | * 21 | * @param sign 22 | * The sign. 23 | * @return The display text. 24 | */ 25 | String[] getDisplayLines(ProtectionSign sign); 26 | 27 | /** 28 | * Gets the type of the sign. 29 | * 30 | * @param sign 31 | * The sign. 32 | * @param side 33 | * The side of the sign to read. 34 | * @return The type of the sign. 35 | */ 36 | Optional getSignType(Sign sign, Side side); 37 | 38 | /** 39 | * Gets the type of the sign from the {@link SignChangeEvent}. The event 40 | * fires before the sign in the world is updated, so trying to read from the 41 | * world will result in outdated information. 42 | * 43 | * @param event 44 | * The sign change event. 45 | * @return The type of the sign. 46 | */ 47 | Optional getSignType(SignChangeEvent event); 48 | 49 | /** 50 | * Gets whether the sign header is valid. Calling this method is the same as 51 | * calling {@code getSignType(sign).isPresent()}. 52 | * 53 | * @param sign 54 | * The sign to check. 55 | * @return True if the header is valid, false otherwise. 56 | */ 57 | boolean hasValidHeader(Sign sign); 58 | 59 | /** 60 | * Parses the given sign for all names on it. 61 | * 62 | * @param signBlock 63 | * The sign to parse. 64 | * @return The parsed sign. 65 | */ 66 | Optional parseSign(Block signBlock); 67 | 68 | /** 69 | * Saves the contents of the given sign to the world. 70 | * 71 | * @param sign 72 | * The sign to save. 73 | */ 74 | void saveSign(ProtectionSign sign); 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/SignType.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker; 2 | 3 | /** 4 | * The different types of signs. 5 | * 6 | */ 7 | public enum SignType { 8 | MORE_USERS, PRIVATE; 9 | 10 | /** 11 | * Gets whether this sign is a main sign: the sign type where exactly 12 | * one is required for each protection. 13 | * 14 | * @return True if this is a main sign, false otherwise. 15 | */ 16 | public boolean isMainSign() { 17 | return this == PRIVATE; 18 | } 19 | } -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/Translator.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker; 2 | 3 | import java.util.List; 4 | import java.util.Locale; 5 | 6 | import org.bukkit.ChatColor; 7 | import org.bukkit.command.CommandSender; 8 | 9 | /** 10 | * Collection of translations. 11 | * 12 | */ 13 | public abstract class Translator { 14 | 15 | public enum Translation { 16 | COMMAND_CANNOT_BE_USED_BY_CONSOLE, 17 | COMMAND_CANNOT_EDIT_OWNER, 18 | COMMAND_LINE_NUMBER_OUT_OF_BOUNDS, 19 | COMMAND_NO_PERMISSION, 20 | COMMAND_NO_SIGN_SELECTED, 21 | COMMAND_PLAYER_NAME_TOO_LONG, 22 | COMMAND_PLUGIN_RELOADED, 23 | COMMAND_SIGN_NO_LONGER_PART_OF_PROTECTION, 24 | COMMAND_UPDATED_SIGN, 25 | PROTECTION_ADD_MORE_USERS_SIGN_INSTEAD, 26 | PROTECTION_BYPASSED, 27 | PROTECTION_CANNOT_CHANGE_SIGN, 28 | PROTECTION_CHEST_HINT, 29 | PROTECTION_CLAIMED_CONTAINER, 30 | PROTECTION_CLAIMED_MANUALLY, 31 | PROTECTION_EXPIRED, 32 | PROTECTION_IN_WILDERNESS, 33 | PROTECTION_IS_CLAIMED_BY, 34 | PROTECTION_NO_ACCESS, 35 | PROTECTION_NO_PERMISSION_FOR_CLAIM, 36 | PROTECTION_NOT_NEARBY, 37 | TAG_EVERYONE, 38 | TAG_MORE_USERS, 39 | TAG_PRIVATE, 40 | TAG_REDSTONE, 41 | TAG_GOLEM, 42 | TAG_TIMER, 43 | UPDATER_MORE_INFORMATION, 44 | UPDATER_UNSUPPORTED_SERVER, 45 | UPDATER_UPDATE_AVAILABLE; 46 | 47 | /** 48 | * Gets the key used in configuration files. 49 | */ 50 | @Override 51 | public String toString() { 52 | return name().replaceFirst("_", ".").toLowerCase(Locale.ROOT); 53 | } 54 | } 55 | 56 | /** 57 | * Returns the translation with the given key. If no such translation 58 | * exists, the key is returned. 59 | * 60 | * @param key 61 | * The key of the translation. 62 | * @return The translation, or the key if not found. 63 | */ 64 | public abstract String get(Translation key); 65 | 66 | /** 67 | * Returns a list of all possible translations. 68 | * 69 | * @param key 70 | * The key of the translation. 71 | * @return A list of all possible translations, or the key (in a list) if not found. 72 | */ 73 | public abstract List getAll(Translation key); 74 | 75 | /** 76 | * Same as {@link #getAll(Translation)}, but with 77 | * {@link ChatColor#stripColor(String)} applied. 78 | * 79 | * @param key 80 | * The key of the translation. 81 | * @return A list of all possible translations, or the key (in a list) if not found. 82 | */ 83 | public abstract List getAllWithoutColor(Translation key); 84 | 85 | /** 86 | * Same as {@link #get(Translation)}, but with 87 | * {@link ChatColor#stripColor(String)} applied. 88 | * 89 | * @param key 90 | * The key of the translation. 91 | * @return The translation, or the key if not found. 92 | */ 93 | public abstract String getWithoutColor(Translation key); 94 | 95 | /** 96 | * Sends the specified message translated to the given player. 97 | * 98 | * @param player 99 | * The player (or console) to the send the message to. 100 | * @param translation 101 | * The message to send. 102 | */ 103 | public abstract void sendMessage(CommandSender player, Translation translation); 104 | 105 | /** 106 | * Sends the specified message translated to the given player. 107 | * 108 | * @param player 109 | * The player (or console) to the send the message to. 110 | * @param translation 111 | * The message to send. 112 | * @param parameters 113 | * Replacements for the message. {0} will be replaced by the 114 | * first parameter, etc. 115 | */ 116 | public final void sendMessage(CommandSender player, Translation translation, String... parameters) { 117 | String translated = get(translation); 118 | for (int i = 0; i < parameters.length; i++) { 119 | translated = translated.replace("{" + i + "}", parameters[i]); 120 | } 121 | player.sendMessage(translated); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/event/PlayerProtectionCreateEvent.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.event; 2 | 3 | import java.util.Objects; 4 | 5 | import org.bukkit.block.Block; 6 | import org.bukkit.entity.Player; 7 | import org.bukkit.event.Cancellable; 8 | import org.bukkit.event.HandlerList; 9 | import org.bukkit.event.player.PlayerEvent; 10 | 11 | /** 12 | * Called just before a protection is created by a player. If you cancel the 13 | * event, the sign will not be placed, or will be popped off. 14 | * 15 | *

16 | * This event is only fired if a player edits a sign to become a [Private] sign, 17 | * or if a player right-clicks a protectable block with a sign in their hand, 18 | * creating a protection. Note that people with world editing tools can bypass 19 | * this event. 20 | */ 21 | public class PlayerProtectionCreateEvent extends PlayerEvent implements Cancellable { 22 | private static final HandlerList handlers = new HandlerList(); 23 | 24 | public static HandlerList getHandlerList() { 25 | return handlers; 26 | } 27 | 28 | private boolean isCancelled = false; 29 | private final Block signBlock; 30 | 31 | public PlayerProtectionCreateEvent(Player player, Block signBlock) { 32 | super(Objects.requireNonNull(player, "player")); 33 | this.signBlock = Objects.requireNonNull(signBlock, "signBlock"); 34 | } 35 | 36 | @Override 37 | public HandlerList getHandlers() { 38 | return handlers; 39 | } 40 | 41 | /** 42 | * Gets the block where the [Private] sign is or will be located. Note that 43 | * there are different ways to create a protection: you can right-click a chest 44 | * with a sign in your hand, or you can manually place a sign and edit that. In 45 | * the first case, no sign will exist yet at this location while in the second 46 | * case, there will already be a sign with some text on it. 47 | * 48 | * @return The block where the [Private] sign is or will be located. 49 | */ 50 | public Block getSignBlock() { 51 | return this.signBlock; 52 | } 53 | 54 | @Override 55 | public boolean isCancelled() { 56 | return this.isCancelled; 57 | } 58 | 59 | @Override 60 | public void setCancelled(boolean cancel) { 61 | this.isCancelled = cancel; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/event/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Events, for consumption by other plugins. 3 | */ 4 | package nl.rutgerkok.blocklocker.event; -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/group/CombinedGroupSystem.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.group; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.List; 6 | 7 | import org.bukkit.entity.Player; 8 | 9 | import com.google.common.base.Preconditions; 10 | 11 | /** 12 | * Class that combines several group systems into one. The 13 | * {@link #isInGroup(Player, String)} method will return true if any of the 14 | * group systems returns true. 15 | * 16 | */ 17 | public final class CombinedGroupSystem extends GroupSystem { 18 | 19 | private List systems; 20 | 21 | /** 22 | * Constructs an initially empty combined group system. 23 | * 24 | * @see #addSystem(GroupSystem) 25 | */ 26 | public CombinedGroupSystem() { 27 | this.systems = new ArrayList<>(); 28 | } 29 | 30 | /** 31 | * Adds a new group system. After calling this method, the 32 | * {@link #isInGroup(Player, String)} method on this object is guaranteed to 33 | * return true if the {@link #isInGroup(Player, String)} on the given group 34 | * system returns true too. too. 35 | * 36 | * @param system 37 | * The group system, may not be null. 38 | */ 39 | public void addSystem(GroupSystem system) { 40 | this.systems.add(Preconditions.checkNotNull(system)); 41 | } 42 | 43 | /** 44 | * @deprecated Just use {@link #addSystem(GroupSystem)} 45 | * @param systems The systems to add. 46 | */ 47 | @Deprecated 48 | public void addSystems(Iterable systems) { 49 | for (GroupSystem system : systems) { 50 | addSystem(system); 51 | } 52 | } 53 | 54 | /** 55 | * Gets all groups that must be kept on reload. 56 | * 57 | * @return All groups that must be kept. 58 | */ 59 | public Collection getReloadSurvivors() { 60 | Collection reloadSurvivors = new ArrayList<>(); 61 | for (GroupSystem system : this.systems) { 62 | if (system.keepOnReload()) { 63 | reloadSurvivors.add(system); 64 | } 65 | } 66 | return reloadSurvivors; 67 | } 68 | 69 | @Override 70 | public boolean isGroupLeader(Player player, String groupName) { 71 | for (GroupSystem system : systems) { 72 | if (system.isGroupLeader(player, groupName)) { 73 | return true; 74 | } 75 | } 76 | return false; 77 | } 78 | 79 | @Override 80 | public boolean isInGroup(Player player, String groupName) { 81 | for (GroupSystem system : systems) { 82 | if (system.isInGroup(player, groupName)) { 83 | return true; 84 | } 85 | } 86 | return false; 87 | } 88 | 89 | @Override 90 | public boolean keepOnReload() { 91 | // BlockLocker will re-add the group system 92 | return false; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/group/GroupSystem.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.group; 2 | 3 | import org.bukkit.entity.Player; 4 | 5 | /** 6 | * Represents a group system on the server. 7 | * 8 | */ 9 | public abstract class GroupSystem { 10 | 11 | /** 12 | * Gets whether the given player is in a group with the given name. The 13 | * implementation should make an effort to make the group name case 14 | * insensitive, but this is not strictly required. 15 | * 16 | * @param player 17 | * The player to check. 18 | * @param groupName 19 | * The name of the group. 20 | * 21 | * @return True if the player is in the given group, false otherwise. 22 | */ 23 | public abstract boolean isInGroup(Player player, String groupName); 24 | 25 | /** 26 | * Gets whether the given player is a leader of the given group. The exact 27 | * definition of a leader is up to the implementation. The implementation 28 | * should make an effort to make the group name case insensitive, but this 29 | * is not strictly required. 30 | * 31 | *

32 | * The default implementation just returns false. 33 | * 34 | * @param player 35 | * The player to check. 36 | * @param groupName 37 | * The name of the group. 38 | * 39 | * @return True if the player is a leader of the given group, false 40 | * otherwise. 41 | */ 42 | public boolean isGroupLeader(Player player, String groupName) { 43 | return false; 44 | } 45 | 46 | /** 47 | * Gets whether this group system must be kept when the plugin is reloaded 48 | * using the reload command of the plugin. 49 | * 50 | *

51 | * When a group system is removed on reload, it must be re-added after the 52 | * reload. For group systems provided by other plugins this is problematic, 53 | * so they must return true. On the other hand, group systems included in 54 | * BlockLocker will be loaded again by BlockLocker, so they must return 55 | * false. 56 | * 57 | * @return True if the group system must be kept on reload, false otherwise. 58 | */ 59 | public boolean keepOnReload() { 60 | // "return true" is the correct option for third-party plugins 61 | return true; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/group/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Whole groups can be given access to a protection. This package represents 3 | * such groups. 4 | * 5 | */ 6 | package nl.rutgerkok.blocklocker.group; -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/ChestSettingsImpl.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Calendar; 5 | import java.util.Date; 6 | import java.util.List; 7 | import java.util.Locale; 8 | import java.util.Objects; 9 | import java.util.Optional; 10 | 11 | import org.bukkit.block.Block; 12 | 13 | import nl.rutgerkok.blocklocker.AttackType; 14 | import nl.rutgerkok.blocklocker.ChestSettings; 15 | import nl.rutgerkok.blocklocker.ProtectableBlocksSettings; 16 | import nl.rutgerkok.blocklocker.ProtectionType; 17 | import nl.rutgerkok.blocklocker.SignType; 18 | import nl.rutgerkok.blocklocker.Translator; 19 | import nl.rutgerkok.blocklocker.Translator.Translation; 20 | 21 | class ChestSettingsImpl implements ChestSettings { 22 | 23 | private static final ProtectionType[] PROTECTION_TYPES = ProtectionType.values(); 24 | 25 | private final Config config; 26 | private final Translator translator; 27 | private final List extraProtectables = new ArrayList<>(); 28 | 29 | ChestSettingsImpl(Translator translator, Config config) { 30 | this.translator = Objects.requireNonNull(translator, "translator"); 31 | this.config = Objects.requireNonNull(config, "config"); 32 | } 33 | 34 | @Override 35 | public boolean allowDestroyBy(AttackType attackType) { 36 | return config.allowDestroyBy(attackType); 37 | } 38 | 39 | @Override 40 | public boolean canProtect(Block block) { 41 | if (config.canProtect(block)) { 42 | return true; 43 | } 44 | if (!this.extraProtectables.isEmpty()) { 45 | for (ProtectableBlocksSettings extra : this.extraProtectables) { 46 | if (extra.canProtect(block)) { 47 | return true; 48 | } 49 | } 50 | } 51 | return false; 52 | } 53 | 54 | @Override 55 | public boolean canProtect(ProtectionType type, Block block) { 56 | if (config.canProtect(type, block)) { 57 | return true; 58 | } 59 | if (!this.extraProtectables.isEmpty()) { 60 | for (ProtectableBlocksSettings extra : this.extraProtectables) { 61 | if (extra.canProtect(type, block)) { 62 | return true; 63 | } 64 | } 65 | } 66 | return false; 67 | } 68 | 69 | @Override 70 | public Optional getChestExpireDate() { 71 | int days = config.getAutoExpireDays(); 72 | if (days <= 0) { 73 | return Optional.empty(); 74 | } 75 | 76 | // Calculate the cutoff date 77 | Calendar calendar = Calendar.getInstance(Locale.US); 78 | calendar.add(Calendar.DAY_OF_MONTH, -days); 79 | Date cutoffDate = calendar.getTime(); 80 | 81 | return Optional.of(cutoffDate); 82 | } 83 | 84 | @Override 85 | public boolean getConnectContainers() { 86 | return this.config.getConnectContainers(); 87 | } 88 | 89 | @Override 90 | public int getDefaultDoorOpenSeconds() { 91 | return config.getDefaultDoorOpenSeconds(); 92 | } 93 | 94 | @Override 95 | public List getExtraProtectables() { 96 | return this.extraProtectables; 97 | } 98 | 99 | @Override 100 | public String getFancyLocalizedHeader(SignType signType, String header) { 101 | List headers = translator.getAll(getTranslationKey(signType)); 102 | 103 | for (String head : headers) { 104 | if (head.equalsIgnoreCase(header)) { 105 | return header; 106 | } 107 | } 108 | 109 | return translator.get(getTranslationKey(signType)); 110 | } 111 | 112 | @Override 113 | public Optional getProtectionType(Block block) { 114 | for (ProtectionType type : PROTECTION_TYPES) { 115 | if (canProtect(type, block)) { 116 | return Optional.of(type); 117 | } 118 | } 119 | return Optional.empty(); 120 | } 121 | 122 | @Override 123 | public List getSimpleLocalizedHeaders(SignType signType) { 124 | return translator.getAllWithoutColor(getTranslationKey(signType)); 125 | } 126 | 127 | private Translation getTranslationKey(SignType signType) { 128 | switch (signType) { 129 | case MORE_USERS: 130 | return Translation.TAG_MORE_USERS; 131 | case PRIVATE: 132 | return Translation.TAG_PRIVATE; 133 | } 134 | throw new AssertionError("Unknown type: " + signType); 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/HopperCacheImpl.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | import org.bukkit.block.Block; 6 | 7 | import com.google.common.cache.Cache; 8 | import com.google.common.cache.CacheBuilder; 9 | 10 | import nl.rutgerkok.blocklocker.ProtectionCache; 11 | 12 | final class HopperCacheImpl implements ProtectionCache { 13 | 14 | private static final long EXPIRE_TIME_SECONDS = 10; 15 | private final Cache redstoneCache; 16 | private final Cache golemCache; 17 | 18 | HopperCacheImpl() { 19 | redstoneCache = CacheBuilder.newBuilder().initialCapacity(1000) 20 | .maximumSize(5000) 21 | .expireAfterWrite(EXPIRE_TIME_SECONDS, TimeUnit.SECONDS) 22 | .build(); 23 | golemCache = CacheBuilder.newBuilder().initialCapacity(1000) 24 | .maximumSize(5000) 25 | .expireAfterWrite(EXPIRE_TIME_SECONDS, TimeUnit.SECONDS) 26 | .build(); 27 | } 28 | 29 | private Cache getCache(CacheType cacheType) { 30 | return switch (cacheType) { 31 | case REDSTONE -> this.redstoneCache; 32 | case GOLEM -> this.golemCache; 33 | }; 34 | } 35 | 36 | @Override 37 | public CacheFlag getAllowed(Block block, CacheType cacheType) { 38 | Boolean isAllowed = getCache(cacheType).getIfPresent(block); 39 | 40 | if (isAllowed == null) { 41 | return CacheFlag.MISS_CACHE; 42 | } 43 | if (isAllowed) { 44 | return CacheFlag.ALLOWED; 45 | } else { 46 | return CacheFlag.NOT_ALLOWED; 47 | } 48 | } 49 | 50 | @Override 51 | public void setAllowed(Block block, CacheType cacheType, boolean allowed) { 52 | getCache(cacheType).put(block, allowed); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/JsonSecretSignEntry.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl; 2 | 3 | import java.util.Objects; 4 | import java.util.Optional; 5 | import java.util.OptionalInt; 6 | import java.util.UUID; 7 | 8 | import com.google.gson.JsonElement; 9 | import com.google.gson.JsonObject; 10 | 11 | import nl.rutgerkok.blocklocker.SecretSignEntry; 12 | 13 | /** 14 | * Implements a secret sign entry on top of JSON. Useful if you need a 15 | * stand-alone implementation for testing. Also used for reading legacy data. 16 | * 17 | */ 18 | public final class JsonSecretSignEntry implements SecretSignEntry { 19 | 20 | private final JsonObject object; 21 | 22 | public JsonSecretSignEntry(JsonObject object) { 23 | this.object = Objects.requireNonNull(object, "object"); 24 | } 25 | 26 | @Override 27 | public Optional getBoolean(String key) { 28 | if (!object.has(key)) { 29 | return Optional.empty(); 30 | } 31 | return Optional.of(object.get(key).getAsBoolean()); 32 | } 33 | 34 | @Override 35 | public OptionalInt getInteger(String key) { 36 | if (!object.has(key)) { 37 | return OptionalInt.empty(); 38 | } 39 | return OptionalInt.of(object.get(key).getAsNumber().intValue()); 40 | } 41 | 42 | @Override 43 | public Optional getString(String key) { 44 | if (!object.has(key)) { 45 | return Optional.empty(); 46 | } 47 | return Optional.of(object.get(key).getAsString()); 48 | } 49 | 50 | @Override 51 | public Optional getUniqueId(String key) { 52 | JsonElement uuidObject = object.get(key); 53 | 54 | if (uuidObject == null || !uuidObject.isJsonPrimitive() || !uuidObject.getAsJsonPrimitive().isString()) { 55 | return Optional.empty(); 56 | } 57 | try { 58 | UUID uuid = UUID.fromString(uuidObject.getAsString()); 59 | return Optional.of(uuid); 60 | } catch (IllegalArgumentException e) { 61 | return Optional.empty(); 62 | } 63 | } 64 | 65 | @Override 66 | public void setBoolean(String key, boolean bool) { 67 | object.addProperty(key, bool); 68 | } 69 | 70 | @Override 71 | public void setInteger(String key, int integer) { 72 | object.addProperty(key, integer); 73 | } 74 | 75 | @Override 76 | public void setString(String key, String value) { 77 | object.addProperty(key, value); 78 | } 79 | 80 | @Override 81 | public void setUniqueId(String key, UUID uuid) { 82 | object.addProperty(key, uuid.toString()); 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/NbtSecretSignEntry.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl; 2 | 3 | import java.lang.ref.WeakReference; 4 | import java.util.Objects; 5 | import java.util.Optional; 6 | import java.util.OptionalInt; 7 | import java.util.UUID; 8 | 9 | import org.bukkit.NamespacedKey; 10 | import org.bukkit.persistence.PersistentDataAdapterContext; 11 | import org.bukkit.persistence.PersistentDataContainer; 12 | import org.bukkit.persistence.PersistentDataType; 13 | import org.bukkit.plugin.Plugin; 14 | import org.bukkit.plugin.java.JavaPlugin; 15 | 16 | import nl.rutgerkok.blocklocker.SecretSignEntry; 17 | 18 | /** 19 | * Implements a secret sign entry on top of NBT tags (using 20 | * {@link PersistentDataContainer}. 21 | */ 22 | public final class NbtSecretSignEntry implements SecretSignEntry { 23 | 24 | private static class SecretSignTagType implements PersistentDataType { 25 | 26 | @Override 27 | public NbtSecretSignEntry fromPrimitive(PersistentDataContainer primitive, 28 | PersistentDataAdapterContext context) { 29 | return new NbtSecretSignEntry(primitive); 30 | } 31 | 32 | @Override 33 | public Class getComplexType() { 34 | return NbtSecretSignEntry.class; 35 | } 36 | 37 | @Override 38 | public Class getPrimitiveType() { 39 | return PersistentDataContainer.class; 40 | } 41 | 42 | @Override 43 | public PersistentDataContainer toPrimitive(NbtSecretSignEntry complex, 44 | PersistentDataAdapterContext context) { 45 | return complex.data; 46 | } 47 | 48 | } 49 | 50 | public static final PersistentDataType TAG_TYPE = new SecretSignTagType(); 51 | 52 | private static final WeakReference PLUGIN = new WeakReference<>( 53 | JavaPlugin.getProvidingPlugin(NbtSecretSignEntry.class)); 54 | 55 | static NamespacedKey key(String name) { 56 | return new NamespacedKey(PLUGIN.get(), name); 57 | } 58 | 59 | private final PersistentDataContainer data; 60 | 61 | public NbtSecretSignEntry(PersistentDataContainer data) { 62 | this.data = Objects.requireNonNull(data); 63 | } 64 | 65 | @Override 66 | public Optional getBoolean(String key) { 67 | Byte result = data.get(key(key), PersistentDataType.BYTE); 68 | if (result == null) { 69 | return Optional.empty(); 70 | } 71 | if (result == 0) { 72 | return Optional.of(Boolean.FALSE); 73 | } 74 | return Optional.of(Boolean.TRUE); 75 | } 76 | 77 | @Override 78 | public OptionalInt getInteger(String key) { 79 | Integer integer = data.get(key(key), PersistentDataType.INTEGER); 80 | if (integer == null) { 81 | return OptionalInt.empty(); 82 | } 83 | return OptionalInt.of(integer.intValue()); 84 | } 85 | 86 | @Override 87 | public Optional getString(String key) { 88 | return Optional.ofNullable(data.get(key(key), PersistentDataType.STRING)); 89 | } 90 | 91 | @Override 92 | public Optional getUniqueId(String key) { 93 | long[] array = data.get(key(key), PersistentDataType.LONG_ARRAY); 94 | if (array == null || array.length != 2) { 95 | return Optional.empty(); 96 | } 97 | return Optional.of(new UUID(array[0], array[1])); 98 | } 99 | 100 | @Override 101 | public void setBoolean(String key, boolean value) { 102 | data.set(key(key), PersistentDataType.BYTE, value ? (byte) 1 : (byte) 0); 103 | } 104 | 105 | @Override 106 | public void setInteger(String key, int integer) { 107 | data.set(key(key), PersistentDataType.INTEGER, integer); 108 | } 109 | 110 | @Override 111 | public void setString(String key, String value) { 112 | data.set(key(key), PersistentDataType.STRING, value); 113 | } 114 | 115 | @Override 116 | public void setUniqueId(String key, UUID uuid) { 117 | long[] array = new long[] { uuid.getMostSignificantBits(), uuid.getLeastSignificantBits() }; 118 | data.set(key(key), PersistentDataType.LONG_ARRAY, array); 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/ProtectionSignImpl.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl; 2 | 3 | import java.util.List; 4 | 5 | import org.bukkit.Location; 6 | 7 | import com.google.common.base.Objects; 8 | import com.google.common.base.Preconditions; 9 | import com.google.common.collect.ImmutableList; 10 | 11 | import nl.rutgerkok.blocklocker.ProtectionSign; 12 | import nl.rutgerkok.blocklocker.SignType; 13 | import nl.rutgerkok.blocklocker.profile.Profile; 14 | 15 | final class ProtectionSignImpl implements ProtectionSign { 16 | 17 | private static final int MAX_PROFILES = 6; 18 | private final SignType signType; 19 | private final List profiles; 20 | private final Location location; 21 | private final boolean requiresResave; 22 | 23 | ProtectionSignImpl(Location location, SignType signType, 24 | List profiles, boolean requiresResave) { 25 | this.location = location; 26 | this.signType = Preconditions.checkNotNull(signType); 27 | this.profiles = ImmutableList.copyOf(profiles); 28 | this.requiresResave = requiresResave; 29 | if (profiles.isEmpty() || profiles.size() > MAX_PROFILES) { 30 | throw new IllegalArgumentException("Invalid size for profiles collection: " + profiles); 31 | } 32 | if (profiles.indexOf(null) != -1) { 33 | throw new IllegalArgumentException("Profiles list contains null profile: " + profiles); 34 | } 35 | } 36 | 37 | @Override 38 | public boolean equals(Object obj) { 39 | if (this == obj) { 40 | return true; 41 | } 42 | if (obj == null) { 43 | return false; 44 | } 45 | if (!(obj instanceof ProtectionSignImpl)) { 46 | return false; 47 | } 48 | ProtectionSignImpl other = (ProtectionSignImpl) obj; 49 | if (!Objects.equal(location.getWorld(), other.location.getWorld())) { 50 | return false; 51 | } 52 | if (location.getBlockX() != other.location.getBlockX()) { 53 | return false; 54 | } 55 | if (location.getBlockY() != other.location.getBlockY()) { 56 | return false; 57 | } 58 | if (location.getBlockZ() != other.location.getBlockZ()) { 59 | return false; 60 | } 61 | return true; 62 | } 63 | 64 | @Override 65 | public Location getLocation() { 66 | // Location is mutable, so always return a clone 67 | return location.clone(); 68 | } 69 | 70 | @Override 71 | public List getProfiles() { 72 | return profiles; 73 | } 74 | 75 | @Override 76 | public SignType getType() { 77 | return signType; 78 | } 79 | 80 | @Override 81 | public int hashCode() { 82 | final int prime = 31; 83 | int result = 1; 84 | result = prime * result + location.getBlockX(); 85 | result = prime * result + location.getBlockY(); 86 | result = prime * result + location.getBlockZ(); 87 | return result; 88 | } 89 | 90 | @Override 91 | public boolean requiresResave() { 92 | return this.requiresResave; 93 | } 94 | 95 | @Override 96 | public ProtectionSign withProfiles(List profiles) { 97 | return new ProtectionSignImpl(location, signType, profiles, requiresResave); 98 | } 99 | 100 | @Override 101 | public ProtectionSign withRequiringResave() { 102 | return new ProtectionSignImpl(location, signType, profiles, true); 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/ProtectionUpdaterImpl.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Objects; 6 | 7 | import javax.annotation.Nullable; 8 | 9 | import org.bukkit.Server; 10 | import org.bukkit.entity.Player; 11 | 12 | import nl.rutgerkok.blocklocker.ProfileFactory; 13 | import nl.rutgerkok.blocklocker.ProtectionSign; 14 | import nl.rutgerkok.blocklocker.ProtectionUpdater; 15 | import nl.rutgerkok.blocklocker.SignParser; 16 | import nl.rutgerkok.blocklocker.profile.PlayerProfile; 17 | import nl.rutgerkok.blocklocker.profile.Profile; 18 | import nl.rutgerkok.blocklocker.protection.Protection; 19 | 20 | public class ProtectionUpdaterImpl implements ProtectionUpdater { 21 | 22 | private final Server server; 23 | private final SignParser signParser; 24 | private final ProfileFactory profileFactory; 25 | 26 | public ProtectionUpdaterImpl(Server server, SignParser signParser, ProfileFactory profileFactory) { 27 | this.server = Objects.requireNonNull(server, "server"); 28 | this.signParser = Objects.requireNonNull(signParser, "signParser"); 29 | this.profileFactory = Objects.requireNonNull(profileFactory, "profileFactory"); 30 | } 31 | 32 | @Nullable 33 | private PlayerProfile getUpdatedProfile(PlayerProfile profile) { 34 | if (profile.getUniqueId().isPresent()) { 35 | Player player = server.getPlayer(profile.getUniqueId().get()); 36 | if (player != null && !player.getName().equals(profile.getDisplayName())) { 37 | // Found a changed name 38 | return profileFactory.fromPlayer(player); 39 | } 40 | return null; 41 | } else { 42 | // Found a missing unique id 43 | String name = profile.getDisplayName(); 44 | if (name.isEmpty()) { 45 | return null; // Empty line, ignore 46 | } 47 | Player player = server.getPlayerExact(name); 48 | if (player == null) { 49 | return null; // No player online with that name, lookup failed 50 | } 51 | return profileFactory.fromPlayer(player); 52 | } 53 | } 54 | 55 | @Override 56 | public void update(Protection protection, boolean newProtection) { 57 | for (ProtectionSign protectionSign : protection.getSigns()) { 58 | updateProtectionSign(protectionSign); 59 | } 60 | } 61 | 62 | @Nullable 63 | private List updateProfiles(ProtectionSign protectionSign) { 64 | List updatedProfiles = null; 65 | 66 | int i = -1; // Will be 0 at first iteration 67 | for (Profile profile : protectionSign.getProfiles()) { 68 | i++; 69 | 70 | if (!(profile instanceof PlayerProfile)) { 71 | continue; 72 | } 73 | PlayerProfile updatedProfile = getUpdatedProfile((PlayerProfile) profile); 74 | if (updatedProfile == null) { 75 | continue; // Nothing to update 76 | } 77 | 78 | if (updatedProfiles == null) { 79 | // Need to initialize list 80 | updatedProfiles = new ArrayList<>(protectionSign.getProfiles()); 81 | } 82 | updatedProfiles.set(i, updatedProfile); 83 | } 84 | 85 | return updatedProfiles; 86 | } 87 | 88 | private void updateProtectionSign(ProtectionSign protectionSign) { 89 | List updatedProfiles = updateProfiles(protectionSign); 90 | 91 | if (updatedProfiles != null) { 92 | protectionSign = protectionSign.withProfiles(updatedProfiles); 93 | } 94 | 95 | if (updatedProfiles != null || protectionSign.requiresResave()) { 96 | signParser.saveSign(protectionSign); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/blockfinder/ConnectedContainersBlockFinder.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.blockfinder; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.bukkit.Material; 7 | import org.bukkit.block.Block; 8 | import org.bukkit.block.BlockFace; 9 | 10 | import nl.rutgerkok.blocklocker.SignParser; 11 | 12 | final class ConnectedContainersBlockFinder extends BlockFinder { 13 | private static final int MAX_SEARCH_DISTANCE = 10; 14 | 15 | ConnectedContainersBlockFinder(SignParser parser) { 16 | super(parser); 17 | } 18 | 19 | @Override 20 | public List findContainerNeighbors(Block block) { 21 | Material containerMaterial = block.getType(); 22 | List blocks = new ArrayList<>(); 23 | blocks.add(block); 24 | 25 | // Search above and below on the starting block 26 | searchVertical(containerMaterial, block, blocks); 27 | 28 | // Just searches in the four cardinal faces, until it hits a block 29 | // of another type. Blocks above and below of the same block type are 30 | // also searched 31 | for (BlockFace face : CARDINAL_FACES) { 32 | int distance = 0; 33 | Block atPosition = block.getRelative(face); 34 | while (distance < MAX_SEARCH_DISTANCE && atPosition.getType() == containerMaterial) { 35 | blocks.add(atPosition); 36 | searchVertical(containerMaterial, atPosition, blocks); 37 | 38 | atPosition = atPosition.getRelative(face); 39 | distance++; 40 | } 41 | } 42 | 43 | BlockFace chestNeighborFace = this.getChestNeighborFaceOrNull(block); 44 | if (chestNeighborFace != null) { 45 | // Double chest, also perform search for other chest block 46 | Block chestNeighbor = block.getRelative(chestNeighborFace); 47 | BlockFace[] searchDirections = { this.turn90Degrees(chestNeighborFace), 48 | this.turn90Degrees(chestNeighborFace).getOppositeFace() }; 49 | for (BlockFace face : searchDirections) { 50 | int distance = 0; 51 | Block atPosition = chestNeighbor.getRelative(face); 52 | while (distance < MAX_SEARCH_DISTANCE && atPosition.getType() == containerMaterial) { 53 | blocks.add(atPosition); 54 | searchVertical(containerMaterial, atPosition, blocks); 55 | 56 | atPosition = atPosition.getRelative(face); 57 | distance++; 58 | } 59 | } 60 | } 61 | 62 | return blocks; 63 | } 64 | 65 | /** 66 | * Searches for blocks of the same type above and below the starting block. 67 | * 68 | * @param containerMaterial 69 | * The material {@link Block#getType() startingBlock.getType()} 70 | * returns. 71 | * @param startingBlock 72 | * The starting block. 73 | * @param blocks 74 | * All connected blocks above and below (so not the starting block 75 | * itself) of the same type will be added to this list. 76 | */ 77 | private void searchVertical(Material containerMaterial, Block startingBlock, List blocks) { 78 | for (BlockFace face : VERTICAL_FACES) { 79 | int distance = 0; 80 | Block atPosition = startingBlock.getRelative(face); 81 | while (distance < MAX_SEARCH_DISTANCE && atPosition.getType() == containerMaterial) { 82 | blocks.add(atPosition); 83 | 84 | atPosition = atPosition.getRelative(face); 85 | distance++; 86 | } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/blockfinder/SeparateContainersBlockFinder.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.blockfinder; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | 6 | import com.google.common.collect.ImmutableList; 7 | 8 | import org.bukkit.block.Block; 9 | import org.bukkit.block.BlockFace; 10 | 11 | import nl.rutgerkok.blocklocker.SignParser; 12 | 13 | final class SeparateContainersBlockFinder extends BlockFinder { 14 | SeparateContainersBlockFinder(SignParser parser) { 15 | super(parser); 16 | } 17 | 18 | @Override 19 | public List findContainerNeighbors(Block block) { 20 | // Currently only chests share an inventory 21 | // Minecraft connects two chests next to each other that have the same 22 | // direction. We simply check for that condition, taking both normal 23 | // and trapped chests into account 24 | BlockFace chestNeighborFace = this.getChestNeighborFaceOrNull(block); 25 | if (chestNeighborFace == null) { 26 | return Collections.singletonList(block); 27 | } 28 | return ImmutableList.of(block, block.getRelative(chestNeighborFace)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/blockfinder/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | /** 5 | * @author Rutger 6 | * 7 | */ 8 | package nl.rutgerkok.blocklocker.impl.blockfinder; -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/event/BlockLockerCommand.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.event; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import org.bukkit.command.Command; 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.command.ConsoleCommandSender; 10 | import org.bukkit.command.TabExecutor; 11 | 12 | import com.google.common.base.Preconditions; 13 | 14 | import nl.rutgerkok.blocklocker.BlockLockerPlugin; 15 | import nl.rutgerkok.blocklocker.Permissions; 16 | import nl.rutgerkok.blocklocker.Translator.Translation; 17 | 18 | public final class BlockLockerCommand implements TabExecutor { 19 | 20 | private final BlockLockerPlugin plugin; 21 | 22 | public BlockLockerCommand(BlockLockerPlugin plugin) { 23 | this.plugin = Preconditions.checkNotNull(plugin); 24 | } 25 | 26 | @Override 27 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 28 | if (args.length == 0) { 29 | return false; 30 | } 31 | 32 | if (args[0].equalsIgnoreCase("reload")) { 33 | return reloadCommand(sender); 34 | } 35 | return false; 36 | } 37 | 38 | @Override 39 | public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { 40 | if (args.length == 2) { 41 | return null; 42 | } 43 | if (args.length > 2) { 44 | return Collections.emptyList(); 45 | } 46 | return Arrays.asList("2", "3", "4"); 47 | } 48 | 49 | private boolean reloadCommand(CommandSender sender) { 50 | if (!sender.hasPermission(Permissions.CAN_RELOAD)) { 51 | plugin.getTranslator().sendMessage(sender, Translation.COMMAND_NO_PERMISSION); 52 | return true; 53 | } 54 | 55 | plugin.reload(); 56 | plugin.getLogger().info(plugin.getTranslator().getWithoutColor(Translation.COMMAND_PLUGIN_RELOADED)); 57 | if (!(sender instanceof ConsoleCommandSender)) { 58 | // Avoid sending message twice to the console 59 | plugin.getTranslator().sendMessage(sender, Translation.COMMAND_PLUGIN_RELOADED); 60 | } 61 | return true; 62 | } 63 | 64 | 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/event/EventListener.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.event; 2 | 3 | import java.util.Collection; 4 | import java.util.Date; 5 | import java.util.Optional; 6 | 7 | import org.apache.commons.lang.Validate; 8 | import org.bukkit.block.Block; 9 | import org.bukkit.event.Listener; 10 | 11 | import nl.rutgerkok.blocklocker.ProtectionCache; 12 | import nl.rutgerkok.blocklocker.SearchMode; 13 | import nl.rutgerkok.blocklocker.impl.BlockLockerPluginImpl; 14 | import nl.rutgerkok.blocklocker.profile.Profile; 15 | import nl.rutgerkok.blocklocker.protection.Protection; 16 | 17 | abstract class EventListener implements Listener { 18 | 19 | final BlockLockerPluginImpl plugin; 20 | 21 | EventListener(BlockLockerPluginImpl plugin) { 22 | Validate.notNull(plugin); 23 | this.plugin = plugin; 24 | } 25 | 26 | boolean anyProtected(Collection blocks) { 27 | for (Block block : blocks) { 28 | if (isProtected(block)) { 29 | return true; 30 | } 31 | } 32 | return false; 33 | } 34 | 35 | boolean isExpired(Protection protection) { 36 | Optional cutoffDate = plugin.getChestSettings().getChestExpireDate(); 37 | if (cutoffDate.isPresent()) { 38 | return protection.isExpired(cutoffDate.get()); 39 | } 40 | return false; 41 | } 42 | 43 | boolean isProtected(Block block) { 44 | return plugin.getProtectionFinder().findProtection(block).isPresent(); 45 | } 46 | 47 | boolean isRedstoneDenied(Block block) { 48 | ProtectionCache.CacheFlag flag = plugin.getProtectionCache().getAllowed(block, ProtectionCache.CacheType.REDSTONE); 49 | if (flag != ProtectionCache.CacheFlag.MISS_CACHE) { 50 | return flag == ProtectionCache.CacheFlag.NOT_ALLOWED; 51 | } else { 52 | Optional protection = plugin.getProtectionFinder().findProtection(block, SearchMode.MAIN_BLOCKS_ONLY); 53 | if (protection.isEmpty()) { 54 | plugin.getProtectionCache().setAllowed(block, ProtectionCache.CacheType.REDSTONE,true); 55 | return false; 56 | } 57 | Profile redstone = plugin.getProfileFactory().fromRedstone(); 58 | boolean allowed = protection.get().isAllowed(redstone); 59 | plugin.getProtectionCache().setAllowed(block, ProtectionCache.CacheType.REDSTONE, allowed); 60 | return !allowed; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/event/GolemListener.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.event; 2 | 3 | import io.papermc.paper.event.entity.ItemTransportingEntityValidateTargetEvent; 4 | import nl.rutgerkok.blocklocker.AttackType; 5 | import nl.rutgerkok.blocklocker.ProtectionCache; 6 | import nl.rutgerkok.blocklocker.SearchMode; 7 | import nl.rutgerkok.blocklocker.impl.BlockLockerPluginImpl; 8 | import nl.rutgerkok.blocklocker.profile.Profile; 9 | import nl.rutgerkok.blocklocker.protection.Protection; 10 | import org.bukkit.block.Block; 11 | import org.bukkit.event.EventHandler; 12 | 13 | import java.util.Optional; 14 | 15 | /** 16 | * Listener for just golems. Separated to allow the plugin to run on older Minecraft versions. In the future, when 17 | * backwards compatibility is no longer necessary, the hopper event could be handled here too, then it would be an 18 | * ExtractListener or something. 19 | */ 20 | public final class GolemListener extends EventListener { 21 | 22 | public GolemListener(BlockLockerPluginImpl plugin) { 23 | super(plugin); 24 | } 25 | 26 | @EventHandler 27 | public void onEntityContainerTarget(ItemTransportingEntityValidateTargetEvent event) { 28 | // Golem handling 29 | if (!event.isAllowed() || plugin.getChestSettings().allowDestroyBy(AttackType.GOLEM)) { 30 | return; 31 | } 32 | 33 | Block block = event.getBlock(); 34 | ProtectionCache cache = this.plugin.getProtectionCache(); 35 | ProtectionCache.CacheFlag cacheFlag = cache.getAllowed(block, ProtectionCache.CacheType.GOLEM); 36 | if (cacheFlag == ProtectionCache.CacheFlag.ALLOWED) { 37 | return; // Don't do anything 38 | } 39 | if (cacheFlag == ProtectionCache.CacheFlag.NOT_ALLOWED) { 40 | event.setAllowed(false); // Prevent targeting 41 | return; 42 | } 43 | 44 | // If we're here, cache miss 45 | // Retrieve the value, and store it in the cache 46 | Optional protection = plugin.getProtectionFinder().findProtection(block, SearchMode.MAIN_BLOCKS_ONLY); 47 | if (protection.isEmpty()) { 48 | cache.setAllowed(block, ProtectionCache.CacheType.GOLEM, false); 49 | return; 50 | } 51 | Profile golemProfile = plugin.getProfileFactory().fromGolem(); 52 | boolean allowed = protection.get().isAllowed(golemProfile); 53 | cache.setAllowed(block, ProtectionCache.CacheType.GOLEM, allowed); 54 | if (!allowed) { 55 | event.setAllowed(false); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/event/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Classes that handle the various events, so that the containers are protected. 3 | * 4 | */ 5 | package nl.rutgerkok.blocklocker.impl.event; -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/group/FactionsGroupSystem.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.group; 2 | 3 | import nl.rutgerkok.blocklocker.group.GroupSystem; 4 | 5 | import org.bukkit.entity.Player; 6 | import org.bukkit.plugin.java.JavaPlugin; 7 | 8 | import com.massivecraft.factions.entity.Faction; 9 | import com.massivecraft.factions.entity.MPlayer; 10 | 11 | /** 12 | * Group system hooking into the Factions plugin by MassiveCraft. 13 | * 14 | */ 15 | public final class FactionsGroupSystem extends GroupSystem { 16 | 17 | /** 18 | * Tests if the Factions plugin is installed. 19 | * 20 | * @return True if the factions plugin is installed, false otherwise. 21 | */ 22 | public static boolean isAvailable() { 23 | try { 24 | JavaPlugin.getProvidingPlugin(MPlayer.class); 25 | return true; 26 | } catch (NoClassDefFoundError e) { 27 | return false; 28 | } 29 | } 30 | 31 | @Override 32 | public boolean isInGroup(Player player, String groupName) { 33 | MPlayer mPlayer = MPlayer.get(player); 34 | Faction faction = mPlayer.getFaction(); 35 | return (faction != null && faction.getName().equalsIgnoreCase(groupName)); 36 | } 37 | 38 | @Override 39 | public boolean keepOnReload() { 40 | // BlockLocker will re-add the group system 41 | return false; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/group/GuildsGroupSystem.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.group; 2 | 3 | import org.bukkit.entity.Player; 4 | import org.bukkit.plugin.java.JavaPlugin; 5 | 6 | import me.glaremasters.guilds.Guilds; 7 | import me.glaremasters.guilds.api.GuildsAPI; 8 | import me.glaremasters.guilds.guild.Guild; 9 | import me.glaremasters.guilds.guild.GuildMember; 10 | import nl.rutgerkok.blocklocker.group.GroupSystem; 11 | 12 | /** 13 | * Group system hooking into the Guilds plugin. 14 | * 15 | */ 16 | public final class GuildsGroupSystem extends GroupSystem { 17 | 18 | /** 19 | * Tests if the Guilds plugin is installed. 20 | * 21 | * @return True if the Guilds plugin is installed, false otherwise. 22 | */ 23 | public static boolean isAvailable() { 24 | try { 25 | JavaPlugin.getProvidingPlugin(GuildsAPI.class); 26 | return true; 27 | } catch (NoClassDefFoundError e) { 28 | return false; 29 | } 30 | } 31 | 32 | @Override 33 | public boolean isGroupLeader(Player player, String groupName) { 34 | Guild guild = Guilds.getApi().getGuild(player); 35 | if (guild == null) { 36 | return false; 37 | } 38 | GuildMember master = guild.getGuildMaster(); 39 | if (master == null) { 40 | // Group has no master, so player is not the group master 41 | return false; 42 | } 43 | if (!master.getUuid().equals(player.getUniqueId())) { 44 | // Player is not het group master 45 | return false; 46 | } 47 | // Player is the group master, but also check name 48 | return guild.getName().equalsIgnoreCase(groupName); 49 | } 50 | 51 | @Override 52 | public boolean isInGroup(Player player, String groupName) { 53 | Guild guild = Guilds.getApi().getGuild(player); 54 | if (guild == null) { 55 | return false; 56 | } 57 | return guild.getName().equalsIgnoreCase(groupName); 58 | } 59 | 60 | @Override 61 | public boolean keepOnReload() { 62 | // BlockLocker will re-add the group system 63 | return false; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/group/PermissionsGroupSystem.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.group; 2 | 3 | import nl.rutgerkok.blocklocker.Permissions; 4 | import nl.rutgerkok.blocklocker.group.GroupSystem; 5 | 6 | import org.bukkit.entity.Player; 7 | 8 | /** 9 | * Considers players with the right group permission node part of a group. 10 | * 11 | */ 12 | public final class PermissionsGroupSystem extends GroupSystem { 13 | 14 | @Override 15 | public boolean isInGroup(Player player, String groupName) { 16 | return player.hasPermission(Permissions.getGroupNode(groupName)); 17 | } 18 | 19 | @Override 20 | public boolean keepOnReload() { 21 | // BlockLocker will re-add the group system 22 | return false; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/group/ScoreboardGroupSystem.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.group; 2 | 3 | import nl.rutgerkok.blocklocker.group.GroupSystem; 4 | 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.entity.Player; 7 | import org.bukkit.scoreboard.Scoreboard; 8 | import org.bukkit.scoreboard.Team; 9 | 10 | /** 11 | * Looks at the teams on the main scoreboard of the server. 12 | * 13 | */ 14 | public final class ScoreboardGroupSystem extends GroupSystem { 15 | 16 | @Override 17 | public boolean isInGroup(Player player, String groupName) { 18 | Scoreboard mainScoreboard = Bukkit.getScoreboardManager().getMainScoreboard(); 19 | Team team = mainScoreboard.getEntryTeam(player.getName()); 20 | if (team == null) { 21 | return false; 22 | } 23 | return team.getName().equalsIgnoreCase(groupName); 24 | } 25 | 26 | @Override 27 | public boolean keepOnReload() { 28 | // BlockLocker will re-add the group system 29 | return false; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/group/SimpleClansGroupSystem.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.group; 2 | 3 | import org.bukkit.entity.Player; 4 | import org.bukkit.plugin.java.JavaPlugin; 5 | 6 | import net.sacredlabyrinth.phaed.simpleclans.Clan; 7 | import net.sacredlabyrinth.phaed.simpleclans.ClanPlayer; 8 | import net.sacredlabyrinth.phaed.simpleclans.SimpleClans; 9 | import nl.rutgerkok.blocklocker.group.GroupSystem; 10 | 11 | /** 12 | * Group system hooking into the SimpleClans plugin. 13 | * 14 | */ 15 | public final class SimpleClansGroupSystem extends GroupSystem { 16 | 17 | /** 18 | * Tests if the SimpleClans plugin is installed. 19 | * 20 | * @return True if the SimpleClans plugin is installed, false otherwise. 21 | */ 22 | public static boolean isAvailable() { 23 | try { 24 | JavaPlugin.getProvidingPlugin(ClanPlayer.class); 25 | return true; 26 | } catch (NoClassDefFoundError e) { 27 | return false; 28 | } 29 | } 30 | 31 | @Override 32 | public boolean isGroupLeader(Player player, String groupName) { 33 | SimpleClans simpleClans = JavaPlugin.getPlugin(SimpleClans.class); 34 | ClanPlayer clanPlayer = simpleClans.getClanManager().getClanPlayer(player.getUniqueId()); 35 | if (!clanPlayer.isLeader()) { 36 | return false; 37 | } 38 | Clan clan = clanPlayer.getClan(); 39 | if (clan == null) { 40 | return false; 41 | } 42 | return clan.getName().equalsIgnoreCase(groupName); 43 | } 44 | 45 | @Override 46 | public boolean isInGroup(Player player, String groupName) { 47 | SimpleClans simpleClans = JavaPlugin.getPlugin(SimpleClans.class); 48 | ClanPlayer clanPlayer = simpleClans.getClanManager().getClanPlayer(player.getUniqueId()); 49 | if (clanPlayer == null) { 50 | return false; 51 | } 52 | return clanPlayer.getClan().getName().equalsIgnoreCase(groupName); 53 | } 54 | 55 | @Override 56 | public boolean keepOnReload() { 57 | // BlockLocker will re-add the group system 58 | return false; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/group/TownyGroupSystem.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.group; 2 | 3 | import org.bukkit.entity.Player; 4 | import org.bukkit.plugin.java.JavaPlugin; 5 | 6 | import com.palmergames.bukkit.towny.Towny; 7 | import com.palmergames.bukkit.towny.TownyUniverse; 8 | import com.palmergames.bukkit.towny.exceptions.NotRegisteredException; 9 | import com.palmergames.bukkit.towny.object.Nation; 10 | import com.palmergames.bukkit.towny.object.Resident; 11 | import com.palmergames.bukkit.towny.object.Town; 12 | 13 | import nl.rutgerkok.blocklocker.group.GroupSystem; 14 | 15 | /** 16 | * Group system hooking into the Factions plugin by MassiveCraft. 17 | * 18 | */ 19 | public final class TownyGroupSystem extends GroupSystem { 20 | 21 | /** 22 | * Tests if the Towny plugin is installed. 23 | * 24 | * @return True if the factions plugin is installed, false otherwise. 25 | */ 26 | public static boolean isAvailable() { 27 | try { 28 | JavaPlugin.getProvidingPlugin(Towny.class); 29 | return true; 30 | } catch (NoClassDefFoundError e) { 31 | return false; 32 | } 33 | } 34 | 35 | @Override 36 | public boolean isGroupLeader(Player player, String groupName) { 37 | try { 38 | Resident resident = TownyUniverse.getInstance().getResident(player.getUniqueId()); 39 | Town town = resident.getTown(); 40 | if (town.getName().equalsIgnoreCase(groupName)) { 41 | if (town.isMayor(resident) || resident.hasTownRank("assistant")) { 42 | return true; 43 | } 44 | } 45 | 46 | Nation nation = town.getNation(); 47 | if (nation.getName().equalsIgnoreCase(groupName)) { 48 | if (nation.isKing(resident) || nation.hasAssistant(resident)) { 49 | return true; 50 | } 51 | } 52 | 53 | return false; 54 | } catch (Exception e) { 55 | // Cannot use catch (NotRegisteredException e) because the class 56 | // cannot be loaded then when Towny isn't present 57 | if (e instanceof NotRegisteredException) { 58 | return false; 59 | } 60 | throw new RuntimeException(e); 61 | } 62 | } 63 | 64 | @Override 65 | public boolean isInGroup(Player player, String groupName) { 66 | try { 67 | Resident resident = TownyUniverse.getInstance().getResident(player.getName()); 68 | Town town = resident.getTown(); 69 | if (town.getName().equalsIgnoreCase(groupName)) { 70 | return true; 71 | } 72 | 73 | Nation nation = town.getNation(); 74 | if (nation.getName().equalsIgnoreCase(groupName)) { 75 | return true; 76 | } 77 | 78 | return false; 79 | } catch (Exception e) { 80 | // Cannot use catch (NotRegisteredException e) because the class 81 | // cannot be loaded then when Towny isn't present 82 | if (e instanceof NotRegisteredException) { 83 | return false; 84 | } 85 | throw new RuntimeException(e); 86 | } 87 | } 88 | 89 | @Override 90 | public boolean keepOnReload() { 91 | // BlockLocker will re-add the group system 92 | return false; 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/group/mcMMOGroupSystem.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.group; 2 | 3 | import java.util.Objects; 4 | 5 | import org.bukkit.entity.Player; 6 | import org.bukkit.plugin.java.JavaPlugin; 7 | 8 | import com.gmail.nossr50.mcMMO; 9 | import com.gmail.nossr50.api.PartyAPI; 10 | 11 | import nl.rutgerkok.blocklocker.group.GroupSystem; 12 | 13 | /** 14 | * Group system hooking into the mcMMO plugin. 15 | * 16 | */ 17 | 18 | public final class mcMMOGroupSystem extends GroupSystem { 19 | 20 | /** 21 | * Tests if the mcMMO plugin is installed. 22 | * 23 | * @return True if the mcMMO plugin is installed, false otherwise. 24 | */ 25 | public static boolean isAvailable() { 26 | try { 27 | JavaPlugin.getProvidingPlugin(mcMMO.class); 28 | return true; 29 | } catch (NoClassDefFoundError e) { 30 | return false; 31 | } 32 | } 33 | 34 | @Override 35 | public boolean isGroupLeader(Player player, String groupName) { 36 | if (!isInGroup(player, groupName)) { 37 | return false; 38 | } 39 | 40 | String leader = PartyAPI.getPartyLeader(groupName); 41 | 42 | return Objects.equals(player.getName(), leader) ? true : false; 43 | } 44 | 45 | @Override 46 | public boolean isInGroup(Player player, String groupName) { 47 | if (!PartyAPI.inParty(player)) { 48 | // Player is not in a party 49 | return false; 50 | } 51 | 52 | String partyName = PartyAPI.getPartyName(player); 53 | 54 | // Ignore case, mcMMO is not case sensitive 55 | return partyName.equalsIgnoreCase(groupName); 56 | } 57 | 58 | @Override 59 | public boolean keepOnReload() { 60 | // BlockLocker will re-add the group system 61 | return false; 62 | } 63 | 64 | } -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/group/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Implementations of group systems. 3 | */ 4 | package nl.rutgerkok.blocklocker.impl.group; -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/location/TownyLocationChecker.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.location; 2 | 3 | import org.bukkit.block.Block; 4 | import org.bukkit.entity.Player; 5 | import org.bukkit.plugin.java.JavaPlugin; 6 | 7 | import com.palmergames.bukkit.towny.Towny; 8 | import com.palmergames.bukkit.towny.TownyAPI; 9 | 10 | import nl.rutgerkok.blocklocker.Translator.Translation; 11 | import nl.rutgerkok.blocklocker.location.IllegalLocationException; 12 | import nl.rutgerkok.blocklocker.location.LocationChecker; 13 | 14 | public final class TownyLocationChecker implements LocationChecker { 15 | 16 | /** 17 | * Tests if the Towny plugin is installed. 18 | * 19 | * @return True if the factions plugin is installed, false otherwise. 20 | */ 21 | public static boolean isAvailable() { 22 | try { 23 | JavaPlugin.getProvidingPlugin(Towny.class); 24 | return true; 25 | } catch (NoClassDefFoundError e) { 26 | return false; 27 | } 28 | } 29 | 30 | @Override 31 | public void checkLocation(Player player, Block block) throws IllegalLocationException { 32 | if (TownyAPI.getInstance().isWilderness(block)) { 33 | throw new IllegalLocationException(Translation.PROTECTION_IN_WILDERNESS); 34 | } 35 | } 36 | 37 | @Override 38 | public boolean keepOnReload() { 39 | return false; // built-in, so it will be re-added on reload 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Internal classes, can change without warning. 3 | * 4 | */ 5 | package nl.rutgerkok.blocklocker.impl; -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/profile/EveryoneProfileImpl.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.profile; 2 | 3 | import java.util.Date; 4 | 5 | import org.apache.commons.lang.Validate; 6 | 7 | import nl.rutgerkok.blocklocker.SecretSignEntry; 8 | import nl.rutgerkok.blocklocker.profile.Profile; 9 | 10 | class EveryoneProfileImpl implements Profile { 11 | 12 | static final String EVERYONE_KEY = "e"; 13 | 14 | private final String tag; 15 | 16 | /** 17 | * Creates a new [Everyone]-profile. 18 | * 19 | * @param translation 20 | * Usually "Everyone", may be localized. 21 | */ 22 | EveryoneProfileImpl(String translation) { 23 | this.tag = translation; 24 | } 25 | 26 | /** 27 | * All instances of this object are equal. 28 | */ 29 | @Override 30 | public boolean equals(Object other) { 31 | if (other == null) { 32 | return false; 33 | } 34 | if (other == this) { 35 | return true; 36 | } 37 | return getClass() == other.getClass(); 38 | } 39 | 40 | @Override 41 | public String getDisplayName() { 42 | return '[' + tag + ']'; 43 | } 44 | 45 | @Override 46 | public void getSaveObject(SecretSignEntry entry) { 47 | entry.setBoolean(EVERYONE_KEY, true); 48 | } 49 | 50 | /** 51 | * All instances of this object are equal. 52 | */ 53 | @Override 54 | public int hashCode() { 55 | return 4; 56 | } 57 | 58 | @Override 59 | public boolean includes(Profile other) { 60 | Validate.notNull(other); 61 | return true; 62 | } 63 | 64 | @Override 65 | public boolean isExpired(Date cutoffDate) { 66 | // The [Everyone] profile never expires 67 | return false; 68 | } 69 | 70 | @Override 71 | public String toString() { 72 | return getClass().getSimpleName(); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/profile/GolemProfileImpl.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.profile; 2 | 3 | import com.google.common.base.Preconditions; 4 | import nl.rutgerkok.blocklocker.SecretSignEntry; 5 | import nl.rutgerkok.blocklocker.profile.Profile; 6 | 7 | import java.util.Date; 8 | 9 | class GolemProfileImpl implements Profile { 10 | 11 | static final String GOLEM_KEY = "go"; 12 | 13 | private final String translatedTag; 14 | 15 | /** 16 | * Creates a new [Golem]-profile. 17 | * 18 | * @param translatedTag 19 | * Usually "Golem", may be localized. 20 | */ 21 | GolemProfileImpl(String translatedTag) { 22 | this.translatedTag = translatedTag; 23 | } 24 | 25 | /** 26 | * All instances of this object are equal. 27 | */ 28 | @Override 29 | public boolean equals(Object other) { 30 | if (other == null) { 31 | return false; 32 | } 33 | if (other == this) { 34 | return true; 35 | } 36 | return getClass() == other.getClass(); 37 | } 38 | 39 | @Override 40 | public String getDisplayName() { 41 | return '[' + translatedTag + ']'; 42 | } 43 | 44 | @Override 45 | public void getSaveObject(SecretSignEntry entry) { 46 | entry.setBoolean(GOLEM_KEY, true); 47 | } 48 | 49 | /** 50 | * All instances of this object are equal. 51 | */ 52 | @Override 53 | public int hashCode() { 54 | return 4000; 55 | } 56 | 57 | @Override 58 | public boolean includes(Profile other) { 59 | Preconditions.checkNotNull(other); 60 | return other instanceof GolemProfileImpl; 61 | } 62 | 63 | @Override 64 | public boolean isExpired(Date cutoffDate) { 65 | // These never expire 66 | return false; 67 | } 68 | 69 | @Override 70 | public String toString() { 71 | return getClass().getSimpleName(); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/profile/GroupLeaderProfileImpl.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.profile; 2 | 3 | import java.util.Date; 4 | import java.util.Locale; 5 | import java.util.Optional; 6 | import java.util.UUID; 7 | 8 | import org.bukkit.Bukkit; 9 | import org.bukkit.entity.Player; 10 | 11 | import com.google.common.base.Preconditions; 12 | 13 | import nl.rutgerkok.blocklocker.SecretSignEntry; 14 | import nl.rutgerkok.blocklocker.group.GroupSystem; 15 | import nl.rutgerkok.blocklocker.profile.PlayerProfile; 16 | import nl.rutgerkok.blocklocker.profile.Profile; 17 | 18 | /** 19 | * Implementation of {@link Profile}. Players are considered part of a group 20 | * when the {@link GroupSystem#isGroupLeader(Player, String)} method returns 21 | * true. 22 | * 23 | */ 24 | class GroupLeaderProfileImpl implements Profile { 25 | 26 | static final String GROUP_LEADER_KEY = "l"; 27 | private String groupName; 28 | private final GroupSystem groupSystem; 29 | 30 | GroupLeaderProfileImpl(GroupSystem groupSystem, String groupName) { 31 | this.groupSystem = Preconditions.checkNotNull(groupSystem); 32 | this.groupName = Preconditions.checkNotNull(groupName); 33 | } 34 | 35 | @Override 36 | public boolean equals(Object other) { 37 | if (other == null) { 38 | return false; 39 | } 40 | if (other == this) { 41 | return true; 42 | } 43 | if (getClass() != other.getClass()) { 44 | return false; 45 | } 46 | 47 | GroupLeaderProfileImpl otherProfile = (GroupLeaderProfileImpl) other; 48 | return groupName.equalsIgnoreCase(otherProfile.groupName); 49 | } 50 | 51 | @Override 52 | public String getDisplayName() { 53 | return "+" + groupName + "+"; 54 | } 55 | 56 | @Override 57 | public void getSaveObject(SecretSignEntry entry) { 58 | entry.setString(GROUP_LEADER_KEY, groupName); 59 | } 60 | 61 | @Override 62 | public int hashCode() { 63 | // Bits are inverted to avoid hash code collision with {@link 64 | // GroupSystem}. 65 | return ~groupName.toLowerCase(Locale.ROOT).hashCode(); 66 | } 67 | 68 | @Override 69 | public boolean includes(Profile other) { 70 | if (!(other instanceof PlayerProfile)) { 71 | return false; 72 | } 73 | 74 | PlayerProfile playerProfile = (PlayerProfile) other; 75 | Optional uuid = playerProfile.getUniqueId(); 76 | if (!uuid.isPresent()) { 77 | return false; 78 | } 79 | 80 | Player player = Bukkit.getPlayer(uuid.get()); 81 | if (player == null) { 82 | return false; 83 | } 84 | 85 | return groupSystem.isGroupLeader(player, groupName); 86 | } 87 | 88 | @Override 89 | public boolean isExpired(Date cutoffDate) { 90 | // Group leader profiles never expire 91 | return false; 92 | } 93 | 94 | @Override 95 | public String toString() { 96 | return getClass().getSimpleName() + "[name=" + groupName + "]"; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/profile/GroupProfileImpl.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.profile; 2 | 3 | import java.util.Date; 4 | import java.util.Locale; 5 | import java.util.Optional; 6 | import java.util.UUID; 7 | 8 | import org.bukkit.Bukkit; 9 | import org.bukkit.entity.Player; 10 | 11 | import com.google.common.base.Preconditions; 12 | 13 | import nl.rutgerkok.blocklocker.SecretSignEntry; 14 | import nl.rutgerkok.blocklocker.group.GroupSystem; 15 | import nl.rutgerkok.blocklocker.profile.GroupProfile; 16 | import nl.rutgerkok.blocklocker.profile.PlayerProfile; 17 | import nl.rutgerkok.blocklocker.profile.Profile; 18 | 19 | /** 20 | * Implementation of {@link GroupProfile}. Players are considered part of a 21 | * group when the name of their scoreboard team matches this group, or when they 22 | * have the permission node for this group. 23 | * 24 | */ 25 | class GroupProfileImpl implements GroupProfile { 26 | 27 | static final String GROUP_KEY = "g"; 28 | private String groupName; 29 | private final GroupSystem groupSystem; 30 | 31 | GroupProfileImpl(GroupSystem groupSystem, String groupName) { 32 | this.groupSystem = Preconditions.checkNotNull(groupSystem); 33 | this.groupName = Preconditions.checkNotNull(groupName); 34 | } 35 | 36 | /** 37 | * We compare uuids or names. Objects are equal if the uuids are present in 38 | * both objects and are equal, or if the uuids are present in neither 39 | * objects and are not equal. 40 | */ 41 | @Override 42 | public boolean equals(Object other) { 43 | if (other == null) { 44 | return false; 45 | } 46 | if (other == this) { 47 | return true; 48 | } 49 | if (getClass() != other.getClass()) { 50 | return false; 51 | } 52 | 53 | GroupProfileImpl otherProfile = (GroupProfileImpl) other; 54 | return groupName.equalsIgnoreCase(otherProfile.groupName); 55 | } 56 | 57 | @Override 58 | public String getDisplayName() { 59 | return "[" + groupName + "]"; 60 | } 61 | 62 | @Override 63 | public void getSaveObject(SecretSignEntry entry) { 64 | entry.setString(GROUP_KEY, groupName); 65 | } 66 | 67 | @Override 68 | public int hashCode() { 69 | return groupName.toLowerCase(Locale.ROOT).hashCode(); 70 | } 71 | 72 | @Override 73 | public boolean includes(Profile other) { 74 | if (!(other instanceof PlayerProfile)) { 75 | return false; 76 | } 77 | 78 | PlayerProfile playerProfile = (PlayerProfile) other; 79 | Optional uuid = playerProfile.getUniqueId(); 80 | if (!uuid.isPresent()) { 81 | return false; 82 | } 83 | 84 | Player player = Bukkit.getPlayer(uuid.get()); 85 | if (player == null) { 86 | return false; 87 | } 88 | 89 | return groupSystem.isInGroup(player, groupName); 90 | } 91 | 92 | @Override 93 | public boolean isExpired(Date cutoffDate) { 94 | // Group profiles never expire 95 | return false; 96 | } 97 | 98 | @Override 99 | public String toString() { 100 | return getClass().getSimpleName() + "[name=" + groupName + "]"; 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/profile/PlayerProfileImpl.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.profile; 2 | 3 | import java.util.Date; 4 | import java.util.Locale; 5 | import java.util.Optional; 6 | import java.util.UUID; 7 | 8 | import org.bukkit.Bukkit; 9 | import org.bukkit.OfflinePlayer; 10 | 11 | import nl.rutgerkok.blocklocker.SecretSignEntry; 12 | import nl.rutgerkok.blocklocker.profile.PlayerProfile; 13 | import nl.rutgerkok.blocklocker.profile.Profile; 14 | 15 | class PlayerProfileImpl implements PlayerProfile { 16 | 17 | static final String NAME_KEY = "n"; 18 | static final String UUID_KEY = "u"; 19 | private String displayName; 20 | private final Optional uuid; 21 | 22 | PlayerProfileImpl(String displayName, Optional uuid) { 23 | this.displayName = displayName; 24 | this.uuid = uuid; 25 | } 26 | 27 | /** 28 | * We compare uuids or names. Objects are equal if the uuids are present in 29 | * both objects and are equal, or if the uuids are present in neither 30 | * objects and are not equal. 31 | */ 32 | @Override 33 | public boolean equals(Object other) { 34 | if (other == null) { 35 | return false; 36 | } 37 | if (other == this) { 38 | return true; 39 | } 40 | if (getClass() != other.getClass()) { 41 | return false; 42 | } 43 | 44 | PlayerProfileImpl otherProfile = (PlayerProfileImpl) other; 45 | if (uuid.isPresent() != otherProfile.uuid.isPresent()) { 46 | return false; 47 | } 48 | 49 | if (uuid.isPresent()) { 50 | return uuid.equals(otherProfile.uuid); 51 | } else { 52 | return displayName.equals(otherProfile.displayName); 53 | } 54 | } 55 | 56 | @Override 57 | public String getDisplayName() { 58 | return displayName; 59 | } 60 | 61 | @Override 62 | public void getSaveObject(SecretSignEntry entry) { 63 | entry.setString(NAME_KEY, displayName); 64 | if (uuid.isPresent()) { 65 | entry.setUniqueId(UUID_KEY, uuid.get()); 66 | } 67 | } 68 | 69 | @Override 70 | public Optional getUniqueId() { 71 | return uuid; 72 | } 73 | 74 | @Override 75 | public int hashCode() { 76 | if (uuid.isPresent()) { 77 | return uuid.hashCode(); 78 | } 79 | return displayName.toLowerCase(Locale.ROOT).hashCode(); 80 | } 81 | 82 | @Override 83 | public boolean includes(Profile other) { 84 | if (!(other instanceof PlayerProfile)) { 85 | return false; 86 | } 87 | 88 | PlayerProfile otherProfile = (PlayerProfile) other; 89 | if (uuid.isPresent()) { 90 | return uuid.equals(otherProfile.getUniqueId()); 91 | } 92 | return displayName.equalsIgnoreCase(otherProfile.getDisplayName()); 93 | } 94 | 95 | @Override 96 | public boolean isExpired(Date cutoffDate) { 97 | 98 | 99 | if (uuid.isPresent()) { 100 | OfflinePlayer player = Bukkit.getOfflinePlayer(uuid.get()); 101 | 102 | if (player.isOnline()) { 103 | return false; 104 | } 105 | if (player.getLastPlayed() > cutoffDate.getTime()) { 106 | return false; 107 | } 108 | 109 | // We know for sure: expired 110 | return true; 111 | } 112 | 113 | // No UUID, so unable to lookup last login 114 | return false; 115 | } 116 | 117 | @Override 118 | public String toString() { 119 | return getClass().getSimpleName() + "[uuid=" + uuid.orElse(null) + ",name=" + displayName + "]"; 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/profile/RedstoneProfileImpl.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.profile; 2 | 3 | import java.util.Date; 4 | 5 | import com.google.common.base.Preconditions; 6 | 7 | import nl.rutgerkok.blocklocker.SecretSignEntry; 8 | import nl.rutgerkok.blocklocker.profile.Profile; 9 | 10 | class RedstoneProfileImpl implements Profile { 11 | 12 | static final String REDSTONE_KEY = "r"; 13 | 14 | private final String translatedTag; 15 | 16 | /** 17 | * Creates a new [Redstone]-profile. 18 | * 19 | * @param translatedTag 20 | * Usually "Redstone", may be localized. 21 | */ 22 | RedstoneProfileImpl(String translatedTag) { 23 | this.translatedTag = translatedTag; 24 | } 25 | 26 | /** 27 | * All instances of this object are equal. 28 | */ 29 | @Override 30 | public boolean equals(Object other) { 31 | if (other == null) { 32 | return false; 33 | } 34 | if (other == this) { 35 | return true; 36 | } 37 | return getClass() == other.getClass(); 38 | } 39 | 40 | @Override 41 | public String getDisplayName() { 42 | return '[' + translatedTag + ']'; 43 | } 44 | 45 | @Override 46 | public void getSaveObject(SecretSignEntry entry) { 47 | entry.setBoolean(REDSTONE_KEY, true); 48 | } 49 | 50 | /** 51 | * All instances of this object are equal. 52 | */ 53 | @Override 54 | public int hashCode() { 55 | return 4; 56 | } 57 | 58 | @Override 59 | public boolean includes(Profile other) { 60 | Preconditions.checkNotNull(other); 61 | return other instanceof RedstoneProfileImpl; 62 | } 63 | 64 | @Override 65 | public boolean isExpired(Date cutoffDate) { 66 | // These never expire 67 | return false; 68 | } 69 | 70 | @Override 71 | public String toString() { 72 | return getClass().getSimpleName(); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/profile/TimerProfileImpl.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.profile; 2 | 3 | import java.util.Date; 4 | 5 | import nl.rutgerkok.blocklocker.SecretSignEntry; 6 | import nl.rutgerkok.blocklocker.profile.Profile; 7 | import nl.rutgerkok.blocklocker.profile.TimerProfile; 8 | 9 | class TimerProfileImpl implements TimerProfile { 10 | 11 | static final String TIME_KEY = "t"; 12 | 13 | private final int seconds; 14 | private final String timerTag; 15 | 16 | TimerProfileImpl(String timerTag, int secondsOpen) { 17 | this.timerTag = timerTag; 18 | 19 | if (secondsOpen < 1) { 20 | secondsOpen = 1; 21 | } else if (secondsOpen > 9) { 22 | secondsOpen = 9; 23 | } 24 | 25 | this.seconds = secondsOpen; 26 | } 27 | 28 | @Override 29 | public String getDisplayName() { 30 | return "[" + timerTag + ":" + seconds + "]"; 31 | } 32 | 33 | @Override 34 | public int getOpenSeconds() { 35 | return seconds; 36 | } 37 | 38 | @Override 39 | public void getSaveObject(SecretSignEntry entry) { 40 | entry.setInteger(TIME_KEY, seconds); 41 | } 42 | 43 | @Override 44 | public boolean includes(Profile other) { 45 | // Includes nobody 46 | return false; 47 | } 48 | 49 | @Override 50 | public boolean isExpired(Date cutoffDate) { 51 | // These never expire 52 | return false; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/profile/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Implementations of profile classes. 3 | * 4 | */ 5 | package nl.rutgerkok.blocklocker.impl.profile; -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/protection/AttachedProtectionImpl.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.protection; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collection; 5 | 6 | import org.bukkit.Sound; 7 | import org.bukkit.block.Block; 8 | import org.bukkit.block.data.BlockData; 9 | import org.bukkit.block.data.Openable; 10 | 11 | import nl.rutgerkok.blocklocker.OpenBlockSound; 12 | import nl.rutgerkok.blocklocker.ProtectionSign; 13 | import nl.rutgerkok.blocklocker.impl.blockfinder.BlockFinder; 14 | import nl.rutgerkok.blocklocker.protection.AttachedProtection; 15 | import nl.rutgerkok.blocklocker.protection.Protection; 16 | 17 | /** 18 | * Implementation of {@link AttachedProtection}. 19 | * 20 | */ 21 | public final class AttachedProtectionImpl extends AbstractProtection implements AttachedProtection { 22 | 23 | /** 24 | * Gets a door protection from a door, with only a single sign looked up. 25 | * 26 | * @param sign 27 | * A hint. If it is a main sign, the owner can easily be looked 28 | * up, speeding up {@link #getOwner()}. 29 | * @param blockFinder 30 | * The block finder. 31 | * @param protectionBlock 32 | * The door. 33 | * 34 | * @return The door protection object. 35 | */ 36 | public static Protection fromBlockWithSign(ProtectionSign sign, BlockFinder blockFinder, Block protectionBlock) { 37 | return new AttachedProtectionImpl(sign, blockFinder, protectionBlock); 38 | } 39 | 40 | /** 41 | * Creates a new protection for a door, with all signs looked up. 42 | * 43 | * @param signs 44 | * All signs of the protection. Collection may not be empty. 45 | * @param blockFinder 46 | * The block finder. 47 | * @param trapDoor 48 | * The door that is protected. 49 | * @return The protection. 50 | */ 51 | public static Protection fromBlockWithSigns(Collection signs, BlockFinder blockFinder, Block trapDoor) { 52 | return new AttachedProtectionImpl(signs, blockFinder, trapDoor); 53 | } 54 | 55 | private static void setBlockOpen(Block block, boolean open) { 56 | BlockData blockData = block.getBlockData(); 57 | if (!isFunctionalOpenable(blockData)) { 58 | return; 59 | } 60 | Openable openable = (Openable) blockData; 61 | 62 | if (openable.isOpen() == open) { 63 | return; 64 | } 65 | 66 | openable.setOpen(open); 67 | block.setBlockData(blockData); 68 | } 69 | private final BlockFinder blockFinder; 70 | 71 | private final Block protectionBlock; 72 | 73 | private AttachedProtectionImpl(Collection signs, BlockFinder blockFinder, Block trapDoor) { 74 | super(signs); 75 | this.protectionBlock = trapDoor; 76 | this.blockFinder = blockFinder; 77 | } 78 | 79 | private AttachedProtectionImpl(ProtectionSign sign, BlockFinder blockFinder, Block trapDoor) { 80 | super(sign); 81 | this.protectionBlock = trapDoor; 82 | this.blockFinder = blockFinder; 83 | } 84 | 85 | @Override 86 | public boolean canBeOpened() { 87 | return isFunctionalOpenable(protectionBlock.getBlockData()); 88 | } 89 | 90 | @Override 91 | protected Collection fetchSigns() { 92 | Block supportingBlock = blockFinder.findSupportingBlock(protectionBlock); 93 | return blockFinder.findAttachedSigns(Arrays.asList(protectionBlock, supportingBlock)); 94 | } 95 | 96 | @Override 97 | public Block getSomeProtectedBlock() { 98 | return protectionBlock; 99 | } 100 | 101 | @Override 102 | public boolean isOpen() { 103 | BlockData materialData = protectionBlock.getBlockData(); 104 | if (isFunctionalOpenable(materialData)) { 105 | return ((Openable) materialData).isOpen(); 106 | } 107 | return false; 108 | } 109 | 110 | @Override 111 | public void setOpen(boolean open, SoundCondition playSound) { 112 | setBlockOpen(protectionBlock, open); 113 | Block supportingBlock = blockFinder.findSupportingBlock(protectionBlock); 114 | setBlockOpen(supportingBlock, open); 115 | 116 | if (playSound == SoundCondition.ALWAYS && isOpen() != open) { 117 | Sound sound = OpenBlockSound.get(protectionBlock.getType(), open); 118 | protectionBlock.getWorld().playSound(protectionBlock.getLocation(), sound, 1f, 0.7f); 119 | } 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/protection/ContainerProtectionImpl.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.protection; 2 | 3 | import java.util.Collection; 4 | import java.util.List; 5 | 6 | import org.bukkit.Sound; 7 | import org.bukkit.block.Block; 8 | import org.bukkit.block.data.BlockData; 9 | import org.bukkit.block.data.Openable; 10 | 11 | import nl.rutgerkok.blocklocker.OpenBlockSound; 12 | import nl.rutgerkok.blocklocker.ProtectionSign; 13 | import nl.rutgerkok.blocklocker.impl.blockfinder.BlockFinder; 14 | import nl.rutgerkok.blocklocker.protection.ContainerProtection; 15 | import nl.rutgerkok.blocklocker.protection.Protection; 16 | 17 | public final class ContainerProtectionImpl extends AbstractProtection implements ContainerProtection { 18 | 19 | /** 20 | * Creates a new protection for the protection block. Calling this method 21 | * might make for a faster {@link #getOwner()}, as it can read the owner 22 | * from the sign if it is a main sign. 23 | * 24 | * @param sign 25 | * The sign. If it is a main sign it is used for 26 | * {@link #getOwner()}. 27 | * @param blockFinder 28 | * The sign finder. 29 | * @param blocks 30 | * The block that are protected. (Usually one block, multiple for 31 | * double chests.) 32 | * 33 | * @return The protection. 34 | */ 35 | public static Protection fromBlocksWithSign(ProtectionSign sign, 36 | Collection blocks, BlockFinder blockFinder) { 37 | return new ContainerProtectionImpl(sign, blocks, blockFinder); 38 | } 39 | 40 | /** 41 | * Creates a new protection for the protection block. Calling this method 42 | * will make for a faster {@link #getAllowed()} and {@link #getOwner()} 43 | * 44 | * @param signs 45 | * All signs in the protection. Collection may not be empty. 46 | * @param blocks 47 | * The blocks that are protected. (Usually one block, multiple 48 | * for double chests.) 49 | * @param blockFinder 50 | * The sign finder. 51 | * 52 | * @return The protection. 53 | */ 54 | public static Protection fromBlocksWithSigns(Collection signs, 55 | List blocks, BlockFinder blockFinder) { 56 | return new ContainerProtectionImpl(signs, blocks, blockFinder); 57 | } 58 | 59 | private final BlockFinder blockFinder; 60 | private final Collection blocks; 61 | 62 | private ContainerProtectionImpl(Collection allSigns, Collection blocks, BlockFinder blockFinder) { 63 | super(allSigns); 64 | this.blocks = blocks; 65 | this.blockFinder = blockFinder; 66 | } 67 | 68 | private ContainerProtectionImpl(ProtectionSign mainSign, Collection blocks, BlockFinder blockFinder) { 69 | super(mainSign); 70 | this.blocks = blocks; 71 | this.blockFinder = blockFinder; 72 | 73 | if (this.blocks.isEmpty()) { 74 | throw new IllegalArgumentException("Blocks list is empty"); 75 | } 76 | } 77 | 78 | @Override 79 | public boolean canBeOpened() { 80 | for (Block block : blocks) { 81 | if (isFunctionalOpenable(block.getBlockData())) { 82 | return true; 83 | } 84 | // Only try first block, as all blocks should be of the same type 85 | return false; 86 | } 87 | return false; 88 | } 89 | 90 | @Override 91 | protected Collection fetchSigns() { 92 | return blockFinder.findAttachedSigns(blocks); 93 | } 94 | 95 | @Override 96 | public Block getSomeProtectedBlock() { 97 | for (Block block : this.blocks) { 98 | return block; 99 | } 100 | throw new AssertionError("Block list was empty, this should have been checked in the constructor"); 101 | } 102 | 103 | @Override 104 | public boolean isOpen() { 105 | for (Block block : blocks) { 106 | BlockData materialData = block.getBlockData(); 107 | if (isFunctionalOpenable(materialData)) { 108 | return ((Openable) materialData).isOpen(); 109 | } 110 | } 111 | return false; 112 | } 113 | 114 | private boolean setBlockOpen(Block block, boolean open) { 115 | BlockData materialData = block.getBlockData(); 116 | if (!isFunctionalOpenable(materialData)) { 117 | return false; 118 | } 119 | 120 | Openable openable = (Openable) materialData; 121 | if (openable.isOpen() == open) { 122 | return false; 123 | } 124 | 125 | // Change the state 126 | openable.setOpen(open); 127 | block.setBlockData(materialData); 128 | return true; 129 | } 130 | 131 | @Override 132 | public void setOpen(boolean open, SoundCondition playSound) { 133 | boolean changed = false; 134 | Block aBlock = null; 135 | for (Block block : blocks) { 136 | changed |= setBlockOpen(block, open); 137 | aBlock = block; 138 | } 139 | if (aBlock == null) { 140 | return; 141 | } 142 | 143 | if (changed && playSound == SoundCondition.ALWAYS) { 144 | Sound sound = OpenBlockSound.get(aBlock.getType(), open); 145 | aBlock.getWorld().playSound(aBlock.getLocation(), sound, 1, 0.7f); 146 | } 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/protection/DoorProtectionImpl.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.protection; 2 | 3 | import java.util.Collection; 4 | 5 | import org.bukkit.block.Block; 6 | 7 | import nl.rutgerkok.blocklocker.ProtectionSign; 8 | import nl.rutgerkok.blocklocker.impl.CompleteDoor; 9 | import nl.rutgerkok.blocklocker.impl.blockfinder.BlockFinder; 10 | import nl.rutgerkok.blocklocker.protection.DoorProtection; 11 | import nl.rutgerkok.blocklocker.protection.Protection; 12 | 13 | /** 14 | * Implementation of {@link DoorProtection}. 15 | * 16 | */ 17 | public final class DoorProtectionImpl extends AbstractProtection implements DoorProtection { 18 | 19 | /** 20 | * Gets a door protection from a door, with only a single sign looked up. 21 | * 22 | * @param sign 23 | * A hint. If it is a main sign, the owner can easily be looked 24 | * up, speeding up {@link #getOwner()}. 25 | * @param blockFinder 26 | * The block finder. 27 | * @param door 28 | * The door. 29 | * 30 | * @return The door protection object. 31 | */ 32 | public static Protection fromDoorWithSign(ProtectionSign sign, BlockFinder blockFinder, CompleteDoor door) { 33 | return new DoorProtectionImpl(sign, blockFinder, door); 34 | } 35 | 36 | /** 37 | * Creates a new protection for a door, with all signs looked up. 38 | * 39 | * @param signs 40 | * All signs of the protection. Collection may not be empty. 41 | * @param blockFinder 42 | * The block finder. 43 | * @param door 44 | * The door that is protected. 45 | * @return The protection. 46 | */ 47 | public static Protection fromDoorWithSigns(Collection signs, BlockFinder blockFinder, CompleteDoor door) { 48 | return new DoorProtectionImpl(signs, blockFinder, door); 49 | } 50 | 51 | private final BlockFinder blockFinder; 52 | private final CompleteDoor door; 53 | 54 | private DoorProtectionImpl(Collection signs, BlockFinder blockFinder, CompleteDoor door) { 55 | super(signs); 56 | this.door = door; 57 | this.blockFinder = blockFinder; 58 | } 59 | 60 | private DoorProtectionImpl(ProtectionSign sign, BlockFinder blockFinder, CompleteDoor door) { 61 | super(sign); 62 | this.door = door; 63 | this.blockFinder = blockFinder; 64 | } 65 | 66 | @Override 67 | public boolean canBeOpened() { 68 | return true; 69 | } 70 | 71 | @Override 72 | protected Collection fetchSigns() { 73 | return blockFinder.findAttachedSigns(door.getBlocksForSigns()); 74 | } 75 | 76 | @Override 77 | public Block getSomeProtectedBlock() { 78 | return this.door.getSomeDoorBlock(); 79 | } 80 | 81 | @Override 82 | public boolean isOpen() { 83 | return door.isOpen(); 84 | } 85 | 86 | @Override 87 | public void setOpen(boolean open, SoundCondition playSound) { 88 | door.setOpen(open, playSound); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/protection/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Implementation for the protection classes. 3 | */ 4 | package nl.rutgerkok.blocklocker.impl.protection; -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/updater/UpdateChecker.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.updater; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.InputStreamReader; 6 | import java.net.HttpURLConnection; 7 | import java.net.URL; 8 | import java.net.URLEncoder; 9 | 10 | import org.bukkit.plugin.Plugin; 11 | 12 | import com.google.common.base.Charsets; 13 | import com.google.gson.JsonObject; 14 | import com.google.gson.JsonParser; 15 | 16 | /** 17 | * Checks whether an update is available. 18 | * 19 | */ 20 | final class UpdateChecker { 21 | 22 | private static final String UPDATE_URL = "https://rutgerkok.nl/tools/updater/blocklocker.php"; 23 | 24 | /** 25 | * Checks online for updates. Blocking method. 26 | * 27 | * @param plugin 28 | * Plugin to check for. 29 | * @return The update result. 30 | * @throws IOException 31 | * If an IO error occurs. 32 | */ 33 | public UpdateCheckResult checkForUpdatesSync(Plugin plugin) throws IOException { 34 | String currentVersionEncoded = URLEncoder.encode(plugin.getDescription().getVersion(), "UTF-8"); 35 | URL url = new URL(UPDATE_URL + "?version=" + currentVersionEncoded); 36 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 37 | connection.setRequestMethod("GET"); 38 | connection.setRequestProperty("Content-Type", "application/json"); 39 | UserAgent.setFor(plugin, connection); 40 | 41 | try (InputStream stream = connection.getInputStream()) { 42 | JsonObject object = JsonParser.parseReader(new InputStreamReader(stream, Charsets.UTF_8)).getAsJsonObject(); 43 | return new UpdateCheckResult(object); 44 | } catch (IOException e) { 45 | // Just rethrow, don't wrap 46 | throw e; 47 | } catch (Exception e) { 48 | throw new IOException(e); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/updater/UpdateNotifier.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.updater; 2 | 3 | import java.net.URL; 4 | import java.util.Optional; 5 | import java.util.Set; 6 | 7 | import com.google.common.base.Joiner; 8 | import com.google.common.base.Preconditions; 9 | 10 | import org.bukkit.command.CommandSender; 11 | import org.bukkit.entity.Player; 12 | import org.bukkit.event.EventHandler; 13 | import org.bukkit.event.EventPriority; 14 | import org.bukkit.event.Listener; 15 | import org.bukkit.event.player.PlayerJoinEvent; 16 | 17 | import nl.rutgerkok.blocklocker.Permissions; 18 | import nl.rutgerkok.blocklocker.Translator; 19 | import nl.rutgerkok.blocklocker.Translator.Translation; 20 | 21 | /** 22 | * Used to notify people that an update of the plugin is out. 23 | * 24 | *

25 | * When registered as a listener, the notification will be send to all admins 26 | * that are logging in. 27 | */ 28 | final class UpdateNotifier implements Listener { 29 | 30 | private final UpdateResult result; 31 | private final Translator translator; 32 | 33 | public UpdateNotifier(Translator translator, UpdateResult result) { 34 | this.result = Preconditions.checkNotNull(result); 35 | this.translator = Preconditions.checkNotNull(translator); 36 | 37 | Preconditions.checkArgument(result.hasNotification(), "result must have a notification"); 38 | 39 | } 40 | 41 | @EventHandler(priority = EventPriority.HIGH) 42 | public void onPlayerJoin(PlayerJoinEvent event) { 43 | Player player = event.getPlayer(); 44 | if (player.hasPermission(Permissions.CAN_BYPASS)) { 45 | sendNotification(player); 46 | } 47 | } 48 | 49 | /** 50 | * Sends a notification to the given person. 51 | * 52 | * @param sender 53 | * The person to send to. 54 | */ 55 | void sendNotification(CommandSender sender) { 56 | UpdateCheckResult checkResult = result.getUpdateCheckResult(); 57 | String newVersion = checkResult.getLatestVersion().orElse("?"); 58 | 59 | // Show status 60 | switch (result.getStatus()) { 61 | case MANUAL_UPDATE: 62 | translator.sendMessage(sender, Translation.UPDATER_UPDATE_AVAILABLE, newVersion); 63 | break; 64 | case UNSUPPORTED_SERVER: 65 | Set mcVersions = result.getUpdateCheckResult().getMinecraftVersions(); 66 | String mcVersionsString = Joiner.on(", ").join(mcVersions); 67 | translator.sendMessage(sender, Translation.UPDATER_UNSUPPORTED_SERVER, newVersion, mcVersionsString); 68 | break; 69 | default: 70 | throw new AssertionError("Umhandled case: " + result.getStatus()); 71 | } 72 | 73 | // More information 74 | Optional infoUrl = checkResult.getInfoUrl(); 75 | if (infoUrl.isPresent()) { 76 | translator.sendMessage(sender, Translation.UPDATER_MORE_INFORMATION, infoUrl.get().toString()); 77 | } 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/updater/UpdatePreference.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.updater; 2 | 3 | import java.util.Locale; 4 | import java.util.Optional; 5 | 6 | /** 7 | * The preference the user set for auto updating. 8 | * 9 | */ 10 | public enum UpdatePreference { 11 | /** 12 | * Automatically check for and install updates. 13 | */ 14 | AUTO_INSTALL, 15 | /** 16 | * Don't check for updates, don't install updates. 17 | */ 18 | DISABLED, 19 | /** 20 | * Check for updates, do nothing else 21 | */ 22 | JUST_NOTIFY; 23 | 24 | /** 25 | * Parses the update preference from the string. The string is 26 | * {@link String#trim() trimmed}, uppercased and spaces are replaced with 27 | * underscores. Then {@link #valueOf(String)} is called, and the result is 28 | * returned as an {@link Optional}. 29 | * 30 | * @param string 31 | * String to parse. 32 | * @return The update preference, or absent if parsing failed. 33 | */ 34 | public static Optional parse(String string) { 35 | string = string.trim().toUpperCase(Locale.ROOT).replace(' ', '_'); 36 | try { 37 | return Optional.of(valueOf(string)); 38 | } catch (IllegalArgumentException e) { 39 | return Optional.empty(); 40 | } 41 | } 42 | 43 | /** 44 | * Gets whether the plugin should check for new updates. 45 | * 46 | * @return True if the plugin should check for updates, false otherwise. 47 | */ 48 | public boolean checkForUpdates() { 49 | return this == AUTO_INSTALL || this == JUST_NOTIFY; 50 | } 51 | 52 | /** 53 | * Gets whether updates should be downloaded and installed automatically. 54 | * 55 | * @return True if updates should be downloaded and installed automatically, 56 | * false otherwise. 57 | */ 58 | public boolean installUpdates() { 59 | return this == AUTO_INSTALL; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/updater/UpdateResult.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.updater; 2 | 3 | import java.io.IOException; 4 | 5 | import com.google.common.base.Preconditions; 6 | import com.google.gson.JsonObject; 7 | 8 | /** 9 | * Result of an update attempt. 10 | * 11 | * @see Updater 12 | * 13 | */ 14 | final class UpdateResult { 15 | 16 | enum Status { 17 | NO_UPDATE, 18 | MANUAL_UPDATE, 19 | CHECK_FAILED, 20 | UNSUPPORTED_SERVER; 21 | 22 | public boolean hasNotification() { 23 | return this == MANUAL_UPDATE 24 | || this == UNSUPPORTED_SERVER; 25 | } 26 | } 27 | 28 | /** 29 | * Gets an update result that indicates that the update failed. 30 | * 31 | * @return The update result. 32 | */ 33 | static UpdateResult failed() { 34 | try { 35 | return new UpdateResult(Status.CHECK_FAILED, new UpdateCheckResult(new JsonObject())); 36 | } catch (IOException e) { 37 | throw new AssertionError(e); 38 | } 39 | } 40 | 41 | private final Status status; 42 | private final UpdateCheckResult checkResult; 43 | 44 | UpdateResult(Status status, UpdateCheckResult checkResult) { 45 | this.status = Preconditions.checkNotNull(status); 46 | this.checkResult = Preconditions.checkNotNull(checkResult); 47 | } 48 | 49 | /** 50 | * Gets the status of the update notification. 51 | * 52 | * @return The status. 53 | */ 54 | Status getStatus() { 55 | return status; 56 | } 57 | 58 | /** 59 | * Gets the result from the update check. 60 | * 61 | * @return The result. 62 | */ 63 | UpdateCheckResult getUpdateCheckResult() { 64 | return checkResult; 65 | } 66 | 67 | /** 68 | * Gets whether this update result has a notification that should be shown. 69 | * 70 | * @return True if this update result has a notification, false otherwise. 71 | */ 72 | boolean hasNotification() { 73 | return status.hasNotification(); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/updater/Updater.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.updater; 2 | 3 | import java.io.IOException; 4 | import java.util.Optional; 5 | import java.util.logging.Level; 6 | import java.util.regex.Matcher; 7 | import java.util.regex.Pattern; 8 | 9 | import com.google.common.base.Preconditions; 10 | import com.google.common.collect.ImmutableSet; 11 | 12 | import nl.rutgerkok.blocklocker.Translator; 13 | import nl.rutgerkok.blocklocker.impl.BlockLockerPluginImpl; 14 | import nl.rutgerkok.blocklocker.impl.updater.UpdateResult.Status; 15 | 16 | /** 17 | * Handles the complete update procedure. 18 | * 19 | */ 20 | public final class Updater { 21 | 22 | /** 23 | * Every twelve hours. 24 | */ 25 | private static final long CHECK_INTERVAL = 20 * 60 * 60 * 12; 26 | 27 | private final BlockLockerPluginImpl plugin; 28 | private volatile UpdatePreference preference; 29 | private final Translator translator; 30 | 31 | public Updater(UpdatePreference preference, Translator translator, BlockLockerPluginImpl plugin) { 32 | this.preference = Preconditions.checkNotNull(preference); 33 | this.translator = Preconditions.checkNotNull(translator); 34 | this.plugin = Preconditions.checkNotNull(plugin); 35 | 36 | } 37 | 38 | private Optional getMinecraftVersion() { 39 | String serverVersion = plugin.getServer().getVersion(); 40 | String regex = "MC\\: *([A-Za-z0-9\\._\\-]+)"; 41 | Matcher matcher = Pattern.compile(regex).matcher(serverVersion); 42 | if (matcher.find() && matcher.groupCount() == 1) { 43 | return Optional.of(matcher.group(1)); 44 | } else { 45 | return Optional.empty(); 46 | } 47 | } 48 | 49 | /** 50 | * Notifies admins of the server of an updated version of the plugin. Can be 51 | * called from any thread. 52 | * 53 | * @param result 54 | * The update result. 55 | */ 56 | private void notifyServer(final UpdateResult result) { 57 | if (!result.hasNotification()) { 58 | return; 59 | } 60 | 61 | // Disable further update checks 62 | preference = UpdatePreference.DISABLED; 63 | 64 | // Notify admins of existing result 65 | if (plugin.getServer().isPrimaryThread()) { 66 | notifyServerFromServerThread(result); 67 | } else { 68 | plugin.runLaterGlobally(new Runnable() { 69 | @Override 70 | public void run() { 71 | notifyServerFromServerThread(result); 72 | } 73 | }, 1); 74 | } 75 | } 76 | 77 | private void notifyServerFromServerThread(UpdateResult result) { 78 | // Must be called from notifyServer 79 | // Result must have a notification 80 | 81 | UpdateNotifier notifier = new UpdateNotifier(translator, result); 82 | 83 | // Notify players 84 | plugin.getServer().getPluginManager().registerEvents(notifier, plugin); 85 | 86 | // Notify console 87 | notifier.sendNotification(plugin.getServer().getConsoleSender()); 88 | } 89 | 90 | /** 91 | * Starts the update process. Does nothing if updates have been disabled. 92 | * 93 | * @throws IllegalStateException 94 | * If this method was called earlier. 95 | */ 96 | public void startUpdater() { 97 | if (!preference.checkForUpdates()) { 98 | return; 99 | } 100 | plugin.runTimerAsync(task -> { 101 | if (preference.checkForUpdates()) { 102 | updateSync(); 103 | } else { 104 | task.cancel(); 105 | } 106 | }, CHECK_INTERVAL); 107 | } 108 | 109 | private void notifyUpdateAvailable(UpdateCheckResult result) throws IOException { 110 | Optional minecraftVersion = getMinecraftVersion(); 111 | 112 | if (minecraftVersion.isEmpty() || result.getMinecraftVersions().containsAll(ImmutableSet.of(minecraftVersion.get()))) { 113 | // Notify that an update is available 114 | notifyServer(new UpdateResult(Status.MANUAL_UPDATE, result)); 115 | } else { 116 | // Server version no longer supported 117 | notifyServer(new UpdateResult(Status.UNSUPPORTED_SERVER, result)); 118 | } 119 | } 120 | 121 | /** 122 | * Blocking update method, must not be called from the server thread. 123 | */ 124 | private void updateSync() { 125 | try { 126 | UpdateChecker checker = new UpdateChecker(); 127 | UpdateCheckResult result = checker.checkForUpdatesSync(plugin); 128 | if (result.needsUpdate()) { 129 | notifyUpdateAvailable(result); 130 | } else { 131 | notifyServer(new UpdateResult(Status.NO_UPDATE, result)); 132 | } 133 | } catch (IOException e) { 134 | plugin.getLogger().log(Level.WARNING, "Error during update check" 135 | + " (you can disable automatic updates in the config file)", e); 136 | notifyServer(UpdateResult.failed()); 137 | } 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/updater/UserAgent.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.updater; 2 | 3 | import java.net.URLConnection; 4 | 5 | import org.bukkit.plugin.Plugin; 6 | 7 | /** 8 | * Sets the correct user agent. 9 | * 10 | */ 11 | final class UserAgent { 12 | 13 | /** 14 | * Sets the correct user agent as used by this plugin on the request. 15 | * 16 | * @param plugin 17 | * The plugin. 18 | * @param connection 19 | * The request to set the user agent on. 20 | */ 21 | static void setFor(Plugin plugin, URLConnection connection) { 22 | String agent = "Mozilla/5.0 (" + plugin.getName() + "/" + plugin.getDescription().getVersion() + ")"; 23 | connection.setRequestProperty("User-Agent", agent); 24 | } 25 | 26 | private UserAgent() { 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/impl/updater/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Classes for updating the plugin. 3 | * 4 | */ 5 | package nl.rutgerkok.blocklocker.impl.updater; -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/location/CombinedLocationChecker.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.location; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.List; 6 | import java.util.Objects; 7 | 8 | import org.bukkit.block.Block; 9 | import org.bukkit.entity.Player; 10 | 11 | /** 12 | * Location checker returns false if any of the given location checkers returns 13 | * false. 14 | * 15 | */ 16 | public final class CombinedLocationChecker implements LocationChecker { 17 | 18 | private final List checkers = new ArrayList<>(); 19 | 20 | /** 21 | * Adds a location checker. 22 | * 23 | * @param checker 24 | * The location checker. 25 | */ 26 | public void addChecker(LocationChecker checker) { 27 | this.checkers.add(Objects.requireNonNull(checker, "checker")); 28 | } 29 | 30 | @Override 31 | public void checkLocation(Player player, Block block) throws IllegalLocationException { 32 | for (LocationChecker checker : this.checkers) { 33 | checker.checkLocation(player, block); 34 | } 35 | } 36 | 37 | /** 38 | * Gets all groups that must be kept on reload. 39 | * 40 | * @return All groups that must be kept. 41 | */ 42 | public Collection getReloadSurvivors() { 43 | Collection reloadSurvivors = new ArrayList<>(); 44 | for (LocationChecker checker : this.checkers) { 45 | if (checker.keepOnReload()) { 46 | reloadSurvivors.add(checker); 47 | } 48 | } 49 | return reloadSurvivors; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/location/IllegalLocationException.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.location; 2 | 3 | import java.util.Objects; 4 | 5 | import nl.rutgerkok.blocklocker.Translator; 6 | import nl.rutgerkok.blocklocker.Translator.Translation; 7 | 8 | public final class IllegalLocationException extends Exception { 9 | 10 | private static final long serialVersionUID = -8935206826802987169L; 11 | 12 | private final Translation translation; 13 | 14 | /** 15 | * Constructor used if you don't want a translated message. 16 | * 17 | * @param message 18 | * The message. 19 | */ 20 | public IllegalLocationException(String message) { 21 | super(Objects.requireNonNull(message, "message")); 22 | this.translation = null; 23 | } 24 | 25 | /** 26 | * Constructor used if you want a translated message. 27 | * 28 | * @param translation 29 | * The translation. 30 | */ 31 | public IllegalLocationException(Translation translation) { 32 | this.translation = Objects.requireNonNull(translation, "translation"); 33 | } 34 | 35 | /** 36 | * Gets a translated message, or the original message if no translation was 37 | * specified. 38 | * 39 | * @param translator 40 | * The translator to use. 41 | * @return The message. 42 | */ 43 | public String getTranslatedMessage(Translator translator) { 44 | if (this.translation != null) { 45 | return translator.get(translation); 46 | } 47 | return getLocalizedMessage(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/location/LocationChecker.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.location; 2 | 3 | import org.bukkit.block.Block; 4 | import org.bukkit.entity.Player; 5 | 6 | import nl.rutgerkok.blocklocker.Permissions; 7 | import nl.rutgerkok.blocklocker.ProtectableBlocksSettings; 8 | 9 | /** 10 | * Used to check if protections can be created at the given location. 11 | * 12 | * @see ProtectableBlocksSettings For making a block protectable in the first 13 | * place. 14 | */ 15 | public interface LocationChecker { 16 | 17 | /** 18 | * Checks whether people can protect chests here. 19 | * 20 | * @param player 21 | * The player placing the chest. 22 | * @param block 23 | * The location. 24 | * @throws IllegalLocationException 25 | * If the player is not allowed to place a chest here. The exception 26 | * should include a message why. 27 | */ 28 | void checkLocation(Player player, Block block) throws IllegalLocationException; 29 | 30 | /** 31 | * Checks whether people can protect chests here. For players that have the 32 | * {@link Permissions#CAN_WILDERNESS} permission node, this is always the case. 33 | * 34 | * @param player 35 | * The player placing the chest. 36 | * @param block 37 | * The location. 38 | * @throws IllegalLocationException 39 | * If the player is not allowed to place a chest here. The exception 40 | * should include a message why. 41 | */ 42 | default void checkLocationAndPermission(Player player, Block block) throws IllegalLocationException { 43 | if (player.hasPermission(Permissions.CAN_WILDERNESS)) { 44 | return; 45 | } 46 | checkLocation(player, block); 47 | } 48 | 49 | /** 50 | * Gets whether this location checker must be kept when the plugin is reloaded 51 | * using the reload command of the plugin. 52 | * 53 | *

54 | * When a location checker is removed on reload, it must be re-added after the 55 | * reload. For location checkers provided by other plugins this is problematic, 56 | * so they must return true. On the other hand, group systems included in 57 | * BlockLocker will be loaded again by BlockLocker, so they must return false. 58 | * 59 | * @return True if the group system must be kept on reload, false otherwise. 60 | */ 61 | default boolean keepOnReload() { 62 | return true; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/profile/GroupProfile.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.profile; 2 | 3 | /** 4 | * Allows a whole group to access a chest. It's up to the implementation who is 5 | * part of the group. 6 | * 7 | */ 8 | public interface GroupProfile extends Profile { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/profile/PlayerProfile.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.profile; 2 | 3 | import java.util.Optional; 4 | import java.util.UUID; 5 | 6 | public interface PlayerProfile extends Profile { 7 | 8 | /** 9 | * Gets the unique id of this player, if any. 10 | * 11 | * @return The unique id. 12 | */ 13 | Optional getUniqueId(); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/profile/Profile.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.profile; 2 | 3 | import java.util.Date; 4 | 5 | import nl.rutgerkok.blocklocker.SecretSignEntry; 6 | 7 | /** 8 | * Represents a profile. A profile is usually a player, but it may also be a whole group or something abstract like 9 | * "Redstone". 10 | * 11 | */ 12 | public interface Profile { 13 | 14 | /** 15 | * Gets the name of the profile as it should be displayed on signs. 16 | * 17 | * @return The name of the profile. 18 | */ 19 | String getDisplayName(); 20 | 21 | /** 22 | * Gets the object as it should be saved. 23 | * 24 | * @param signEntry 25 | * The entry to save to. 26 | */ 27 | void getSaveObject(SecretSignEntry signEntry); 28 | 29 | /** 30 | * Gets whether this profile includes another profile. For example, 31 | * "[Everyone]" includes all other profiles, and "jeb_" includes everyone 32 | * with the name jeb_. 33 | * 34 | * @param other 35 | * The other profile. 36 | * @return True if this profile includes the other profile, false otherwise. 37 | */ 38 | boolean includes(Profile other); 39 | 40 | /** 41 | * Gets whether this profile is expired. 42 | * 43 | * @param cutoffDate 44 | * The cutoff date: there must be activity for this profile after 45 | * the given date, or else it's considered expired. 46 | * @return True if the profile is expired, false otherwise. 47 | */ 48 | boolean isExpired(Date cutoffDate); 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/profile/TimerProfile.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.profile; 2 | 3 | /** 4 | * A profile that allows nobody, but sets the time how long the door may be 5 | * open. 6 | * 7 | */ 8 | public interface TimerProfile extends Profile { 9 | 10 | /** 11 | * Gets the amount of ticks the door can be open before it is closed again. 12 | * 13 | * @return The amount of ticks. 14 | */ 15 | int getOpenSeconds(); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/profile/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Classes representing profiles (chest owners): players, groups, etc. 3 | * 4 | */ 5 | package nl.rutgerkok.blocklocker.profile; -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/protection/AttachedProtection.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.protection; 2 | 3 | /** 4 | * A protection that is attached to another block; signs are searched for on 5 | * that other block. 6 | * 7 | */ 8 | public interface AttachedProtection extends Protection { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/protection/ContainerProtection.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.protection; 2 | 3 | /** 4 | * Represents a protected container, like a chest or a furnace. 5 | * 6 | */ 7 | public interface ContainerProtection extends Protection { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/protection/DoorProtection.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.protection; 2 | 3 | 4 | /** 5 | * Represents a protection for a door. Keep in mind that trapdoors are doors 6 | * too. 7 | * 8 | */ 9 | public interface DoorProtection extends Protection { 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/protection/Protection.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.protection; 2 | 3 | import java.util.Collection; 4 | import java.util.Date; 5 | import java.util.Optional; 6 | 7 | import org.bukkit.block.Block; 8 | 9 | import nl.rutgerkok.blocklocker.ProtectionSign; 10 | import nl.rutgerkok.blocklocker.profile.Profile; 11 | 12 | /** 13 | * Represents a generic protection container with its attached signs. 14 | * 15 | */ 16 | public interface Protection { 17 | 18 | /** 19 | * When to play a sound for opening a door. 20 | * 21 | */ 22 | enum SoundCondition { 23 | /** 24 | * Never play a sound. 25 | */ 26 | NEVER, 27 | /** 28 | * Only play a sound for doors that the Minecraft client doesn't 29 | * automatically play a sound for a player opens it. 30 | */ 31 | AUTOMATIC, 32 | /** 33 | * Always play a sound. 34 | */ 35 | ALWAYS 36 | } 37 | 38 | /** 39 | * Gets whether the protection can be opened as a door/gate/trapdoor. 40 | * @return True if the protection can be opened, false otherwise. 41 | */ 42 | boolean canBeOpened(); 43 | 44 | /** 45 | * Gets the allowed profiles in this protection. Allowed profiles can 46 | * place/take items and open doors, but cannot break the protection. This 47 | * list includes the {@link #getOwner() owner}. 48 | * 49 | * @return The allowed profiles. 50 | * @see #isAllowed(Profile) 51 | */ 52 | Collection getAllowed(); 53 | 54 | /** 55 | * Gets the location of the protection. Returns one of the blocks in the 56 | * protection (if there are multiple blocks), and not one of the signs. 57 | * 58 | * @return The location. 59 | */ 60 | Block getSomeProtectedBlock(); 61 | 62 | /** 63 | * Gets the amount of seconds the door should stay open, before closing 64 | * automatically. If no amount of seconds was specified on the door, -1 is 65 | * returned. 66 | * 67 | * @return The amount of ticks, or -1 if unspecified. 68 | */ 69 | int getOpenSeconds(); 70 | 71 | /** 72 | * Gets the owner of this protection. The owner is the only one allowed to 73 | * destroy this protection. All protections have an owner, unless they have 74 | * been corrupted by for example a world editor. 75 | * 76 | * @return The owner. 77 | * @see #isOwner(Profile) 78 | */ 79 | Optional getOwner(); 80 | 81 | /** 82 | * Gets the display name of the owner of the protection. If 83 | * {@link #getOwner()} is absent, "?" is returned. 84 | * 85 | * @return The display name. 86 | */ 87 | String getOwnerDisplayName(); 88 | 89 | /** 90 | * Gets all signs used in this protection. Low-level method, 91 | * {@link #getAllowed()} is preferred. Example usage is changing the 92 | * contents on the sign. 93 | * 94 | * @return All signs used by this protection. 95 | */ 96 | Collection getSigns(); 97 | 98 | /** 99 | * Checks if the given profile has been allowed to this protection. 100 | * 101 | * @param profile 102 | * The profile to check. 103 | * @return True if the profile is allowed, false otherwise. 104 | * @see #getAllowed() 105 | */ 106 | boolean isAllowed(Profile profile); 107 | 108 | /** 109 | * Gets whether this protection is expired. As protection is expired when 110 | * the {@link #getOwner() owner profile} is considered 111 | * {@link Profile#isExpired(Date) expired}. 112 | * 113 | * @param cutoffDate 114 | * The cutoff date. 115 | * @return True if the profile is expired, false otherwise. 116 | */ 117 | boolean isExpired(Date cutoffDate); 118 | 119 | /** 120 | * Gets whether the door is currently opened. If only parts of the door are 121 | * open, the result of this method is undefined. 122 | * 123 | * @return Whether the door is currently opened. 124 | */ 125 | boolean isOpen(); 126 | 127 | /** 128 | * Checks if the given profile is the owner of this chest. 129 | * 130 | * @param profile 131 | * The profile to check. 132 | * @return True if it is the owner, false otherwise. 133 | * @see #getOwner() 134 | */ 135 | boolean isOwner(Profile profile); 136 | 137 | /** 138 | * Opens or closes the door, as specified. Plays no sound. 139 | * 140 | * @param open 141 | * True to open the door, false otherwise. 142 | * @see #setOpen(boolean, SoundCondition) Control over sound playing. 143 | */ 144 | default void setOpen(boolean open) { 145 | setOpen(open, SoundCondition.NEVER); 146 | } 147 | 148 | /** 149 | * Opens or closes the door, as specified. 150 | * 151 | * @param open 152 | * True to open the door, false otherwise. 153 | * @param playSound 154 | * Whether to play a sound. 155 | */ 156 | void setOpen(boolean open, SoundCondition playSound); 157 | 158 | } 159 | -------------------------------------------------------------------------------- /src/main/java/nl/rutgerkok/blocklocker/protection/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Classes representing protections. 3 | * 4 | */ 5 | package nl.rutgerkok.blocklocker.protection; -------------------------------------------------------------------------------- /src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: ${project.name} 2 | version: "${project.version}" 3 | main: nl.rutgerkok.blocklocker.impl.BlockLockerPluginImpl 4 | author: Rutger Kok 5 | load: startup 6 | api-version: "1.20" 7 | softdepend: [Guilds, Factions, mcMMO, Towny] 8 | folia-supported: true 9 | permissions: 10 | blocklocker.protect: 11 | description: "Protect containers and doors." 12 | default: true 13 | blocklocker.bypass: 14 | description: "Bypass someone else's protection, but don't allow destroying it." 15 | default: op 16 | blocklocker.admin: 17 | description: "Edit someone's protection signs, or remove the protection entirely." 18 | default: op 19 | blocklocker.reload: 20 | description: "Reload the plugin using /blocklocker reload." 21 | default: op 22 | blocklocker.wilderness: 23 | description: "Place chests in the wilderness (outside of Towny claims)" 24 | default: true 25 | commands: 26 | blocklocker: 27 | description: "Administration commands for BlockLocker." 28 | permission: "blocklocker.reload" 29 | usage: "/ reload" 30 | aliases: "bl" 31 | -------------------------------------------------------------------------------- /src/main/resources/translations-cs.yml: -------------------------------------------------------------------------------- 1 | # Translations file for the Czech language. Translations by PetyXbron. 2 | command: 3 | cannot_be_used_by_console: "Tento příkaz nelze býti použit konzolí. Promiň!" 4 | cannot_edit_owner: "&4Nemůžeš změnit majitele zabezpečení Místo toho znič ceduli." 5 | line_number_out_of_bounds: "&4Zadej prosím číslo řádku od 2 po 4." 6 | no_sign_selected: "&4Nejdříve musíš vybrat cedulku jednoho z tvých zabezpečených bloků pravým klikem na myši." 7 | no_permission: "&4Nemáš právo použít tenhle příkaz. Promiň!" 8 | player_name_too_long: "&4Jméno hráče obsahuje více než 16 znaků, což se nevejde na cedulku. Je také delší, než limit u přezdívek hráčů Mojang." 9 | plugin_reloaded: "&6Znovu-načteny konfigurační soubory!" 10 | sign_no_longer_part_of_protection: "&4Vybraná cedulka již nespadá pod zabezpečený blok." 11 | updated_sign: "&6Cedulka aktualizována!" 12 | protection: 13 | add_more_users_sign_instead: "&4Na zabezpečeném bloku může být pouze jedna [Private] cedulka. Místo toho přidej [More Users] cedulku." 14 | bypassed: "&6Právě jsi obešel zabezpečení hráče {0}." 15 | can_only_add_protection_sign: "&4Na zabezpečeném bloku může být pouze jedna [Private] a nula nebo více [More Users] cedulek. Jiné cedulky nejsou povoleny." 16 | cannot_change_sign: "&4Nemůžeš změnit cedulku poblíž zabezpečeného bloku." 17 | chest_hint: "&6Umísti na tuhle bednu cedulku k zabezpečení před ostatními hráči." 18 | claimed_container: "&6Blok zabezpečen. Nikdo jiný k ní teď nemá přístup." 19 | claimed_manually: "&6Block zabezpečen. Přístup k němu mají pouze hráči uvedeni na cedulce." 20 | expired: "&6Zabezpečení tohoto bloku vypršela kvůli nečinnosti vlastníka." 21 | in_wilderness: "&4Zde nemůžeš vytvořit zabezpečení bloku: nacházíš se v divočině." 22 | is_claimed_by: "&6Tento blok je zabezpečen hráčem {0}." 23 | no_access: "&4Tento blok nemůžeš použít, protože je zabezpečen hráčem {0}." 24 | no_permission_for_claim: "&4Nemáš oprávnění k zabezpečení bloku, promiň." 25 | not_nearby: "&4Ve tvé blízkosti se nenachází žádný zabezpečitelný blok. Použij (shift a) pravé tlačítko na myši s cedulkou v ruce k jejímu položení a zabezpečení bloku." 26 | tag: 27 | everyone: 28 | - "Everyone" 29 | - "Všichni" 30 | timer: 31 | - "Timer" 32 | - "Časovač" 33 | redstone: 34 | - "Redstone" 35 | - "Rudit" 36 | private: 37 | - "[Private]" 38 | - "[Soukromé]" 39 | more_users: 40 | - "[More Users]" 41 | - "[Více uživatelů]" 42 | updater: 43 | more_information: "&6Více informací: {0}" 44 | unsupported_server: "&4Nejnovější verze ${project.name} (verze {0}) již nepodporuje tuto verzi Minecraftu, pouze Minecraft {1}." 45 | update_available: "&6Aktualizace pro ${project.name} (verze {0}) je k dispozici pro ruční stáhnutí a instalaci." 46 | updated_automatically: "&6Je k dispozici nová aktualizace pro ${project.name} (verze {0}). Po restartování serveru se automaticky nainstaluje." 47 | -------------------------------------------------------------------------------- /src/main/resources/translations-de.yml: -------------------------------------------------------------------------------- 1 | # Übersetzung in die deutsche Sprache 2 | command: 3 | cannot_be_used_by_console: "Dieser Befehl kann nicht über die Konsole verwendet werden!" 4 | cannot_edit_owner: "&4Du kannst den Besitzer dieses Schildes nicht ändern. Du solltest das Schild stattdessen zerstören." 5 | line_number_out_of_bounds: "&4Bitte gib eine Zeilennummer von 2 bis 4 ein." 6 | no_sign_selected: "&4Du musst zuerst ein Schild auf einem deiner geschützten Blöcke auswählen, indem du mit der rechten Maustaste darauf klickst." 7 | no_permission: "&4Du hast nicht die Berechtigung, diesen Befehl auszuführen." 8 | player_name_too_long: "&4Der Name besteht aus mehr als 16 Zeichen (länger als Mojang erlaubt), weshalb er nicht auf das Schild passt." 9 | plugin_reloaded: "&6Konfiguration neu geladen!" 10 | sign_no_longer_part_of_protection: "&4Das ausgewählte Schild gehört nicht mehr zu einem geschützten Block." 11 | updated_sign: "&6Das Schild wurde aktualisiert!" 12 | protection: 13 | add_more_users_sign_instead: "&4Es ist nur ein Schild mit [Privat] für einen Schutz möglich. Füge stattdessen ein Schild mit [Mehr Benutzer] hinzu." 14 | bypassed: "&6Du hast einen Schutz von {0} umgangen." 15 | can_only_add_protection_sign: "&4Es kann nur ein [Privat] oder [Mehr Benutzer] Schild an diesem Block platziert werden. Mehr geht nicht." 16 | cannot_change_sign: "&4Du kannst ein Schild nicht in der Nähe eines beanspruchten Behälters ändern." 17 | chest_hint: "&6Platziere ein Schild an dieser Truhe, um sie zu schützen." 18 | claimed_container: "&6Block bzw. Bereich geschützt." 19 | claimed_manually: "&6Geschützt! Nur die auf dem Schild aufgeführten Personen können darauf zugreifen." 20 | expired: "&6Der Schutz dieses Blocks wurde aufgrund von Inaktivität des Besitzers aufgehoben." 21 | in_wilderness: "&4Du befindest dich in der Wildnis, daher kannst du hier keinen Schutz erstellen." 22 | is_claimed_by: "&6Dieser Block gehört {0}." 23 | no_access: "&4Du kannst diesen Block nicht verwenden, er ist durch {0} geschützt." 24 | no_permission_for_claim: "&4Du hast nicht die Berechtigung, einen Behälter zu schützen." 25 | not_nearby: "&4Es ist kein zu schützender Block in der Nähe. (Umschalt+)Rechtsklick auf einen Behälter mit einem Schild in deiner Hand, um ihn zu schützen." 26 | selected_sign: "&6Schild ausgewählt. Gib \"/blocklocker \" ein, um das gewählte Schild zu bearbeiten. Alternativ füge Folgendes hinzu: &n[Redstone]&r&6 &n[Jeder]&r&6." 27 | tag: 28 | everyone: ["Jeder", "Everyone"] 29 | timer: "Timer" 30 | redstone: "Redstone" 31 | private: ["[Privat]", "[Private]"] 32 | more_users: ["[Mehr Benutzer]", "[More Users]"] 33 | updater: 34 | more_information: "&6Mehr Informationen: {0}" 35 | unsupported_server: "&4Die neueste Version von ${project.name} (Version {0}) unterstützt diese Minecraft-Version nicht mehr, sondern nur noch Minecraft {1}." 36 | update_available: "&6Eine Aktualisierung für ${project.name} (Version {0}) steht zum manuellen Herunterladen bereit." 37 | updated_automatically: "&6Ein Update wird beim nästen Server-Neustart installiert." 38 | -------------------------------------------------------------------------------- /src/main/resources/translations-en.yml: -------------------------------------------------------------------------------- 1 | # Translations file for the English language 2 | command: 3 | cannot_be_used_by_console: "This command cannot be used by the console. Sorry!" 4 | cannot_edit_owner: "&4You cannot change the owner of the protection. Destroy the sign instead." 5 | line_number_out_of_bounds: "&4Please enter a line number between 2 and 4, inclusive." 6 | no_sign_selected: "&4You must select a sign on one of your protected blocks first by right-clicking it." 7 | no_permission: "&4You don't have permission to execute this command. Sorry!" 8 | player_name_too_long: "&4The player name is over 16 characters, which doesn't fit on the sign. It is also longer than Mojang allows player names to be." 9 | plugin_reloaded: "&6Reloaded the configuration files!" 10 | sign_no_longer_part_of_protection: "&4The selected sign no longer belongs to a protected block." 11 | updated_sign: "&6Updated the sign!" 12 | protection: 13 | add_more_users_sign_instead: "&4There can only be one [Private] sign on a protection. Add a [More Users] sign instead." 14 | bypassed: "&6You have just bypassed a protection of {0}." 15 | can_only_add_protection_sign: "&4There can only be one [Private] and zero or more [More Users] signs on a protection. Other signs are not allowed." 16 | cannot_change_sign: "&4You cannot change a sign nearby a claimed container." 17 | chest_hint: "&6Place a sign on this chest to protect it." 18 | claimed_container: "&6Claimed this container. Nobody else can access it." 19 | claimed_manually: "&6Created a claim. Only the people listed on the sign can access it." 20 | expired: "&6The protection of this block has expired due to inactivity of the owner." 21 | in_wilderness: "&4You cannot create a protection here: you are in the wilderness." 22 | is_claimed_by: "&6This block has been claimed by {0}." 23 | no_access: "&4You cannot use this block, it is protected by {0}." 24 | no_permission_for_claim: "&4You don't have permission to claim a container, sorry." 25 | not_nearby: "&4There is no protectable block nearby. (Shift-)right-click a container with a sign in your hand to protect it." 26 | tag: 27 | everyone: "Everyone" 28 | timer: "Timer" 29 | redstone: "Redstone" 30 | golem: 31 | - Golem 32 | - Golems 33 | private: "[Private]" 34 | more_users: "[More Users]" 35 | updater: 36 | more_information: "&6More information: {0}" 37 | unsupported_server: "&4The latest version of ${project.name} (version {0}) no longer supports this Minecraft version, only Minecraft {1}." 38 | update_available: "&6An update for ${project.name} (version {0}) is available for manual download." 39 | updated_automatically: "&6An update for ${project.name} is available (version {0}). It will automatically be installed when you restart the server." 40 | -------------------------------------------------------------------------------- /src/main/resources/translations-es.yml: -------------------------------------------------------------------------------- 1 | # Translation file for the Spanish language. Translations by TheCiROMG. 2 | command: 3 | cannot_be_used_by_console: Este comando no puede ser utilizado por la consola. ¡Lo siento! 4 | cannot_edit_owner: '&4No puede cambiar el propietario de la protección. Destruye el cartel en su lugar.' 5 | line_number_out_of_bounds: '&4Ingrese un número de línea entre 2 y 4, el 1 esta en uso.' 6 | no_permission: '&4No tienes permiso para ejecutar este comando. ¡Lo siento!' 7 | no_sign_selected: '&4Debe seleccionar un cartel en uno de sus bloques protegidos primero, haciendo clic derecho.' 8 | player_name_too_long: '&4El nombre del jugador tiene más de 16 caracteres, lo que no cabe en el letrero. También es más largo de lo que Mojang permite que los nombres de los jugadores sean.' 9 | plugin_reloaded: '&6¡Recargo los archivos de configuración!' 10 | sign_no_longer_part_of_protection: '&4El letrero seleccionado ya no pertenece a un bloque protegido.' 11 | updated_sign: '&6Letrero actualizado!' 12 | protection: 13 | add_more_users_sign_instead: '&4Solo puede haber un cartel [Privado] en una protección. Agregue un cartel [Más usuarios] en su lugar.' 14 | bypassed: '&6Acaba de pasar por alto una protección de {0}.' 15 | cannot_change_sign: '&4No puede cambiar un letrero en un contenedor reclamado.' 16 | chest_hint: '&6Coloque un letrero en este cofre para protegerlo.' 17 | claimed_container: '&6Reclamó este contenedor. Nadie más puede acceder.' 18 | claimed_manually: '&6Creó una proteccion. Solo las personas que figuran en el letrero pueden acceder.' 19 | expired: '&6La protección de este bloque ha expirado debido a la inactividad del propietario.' 20 | in_wilderness: '&4No puedes crear una protección aquí: estás en el desierto.' 21 | is_claimed_by: '&6Este bloque ha sido reclamado por {0}.' 22 | no_access: '&4No puedes usar este bloque, está protegido por {0}.' 23 | no_permission_for_claim: '&4No tiene permiso para reclamar un contenedor, lo siento.' 24 | not_nearby: '&4No hay bloque protector cerca. Haz clic derecho en un contenedor con un letrero en tu mano para protegerlo.' 25 | selected_sign: '&6Cartel seleccionado. Escribe "/blocklocker " para editar este cartel.' 26 | tag: 27 | everyone: Todos 28 | more_users: 29 | - '[Mas Usuarios]' 30 | - '[More Users]' 31 | private: 32 | - '[Privado]' 33 | - '[Private]' 34 | redstone: Redstone 35 | timer: Timer 36 | updater: 37 | more_information: '&6Más información: {0}' 38 | unsupported_server: '&4La última versión de BlockLocker (version {0}) ya no es compatible con esta versión de Minecraft, solo Minecraft {1}.' 39 | update_available: '&6Una actualización para BlockLocker (version {0}) está disponible para su descarga manual.' 40 | -------------------------------------------------------------------------------- /src/main/resources/translations-fr.yml: -------------------------------------------------------------------------------- 1 | # Translation file for the French language. Translations by Kervinou. 2 | command: 3 | cannot_be_used_by_console: "Cette commande n'est pas possible à partir de la console !" 4 | cannot_edit_owner: "&4Vous ne pouvez pas changer le propriétaire de cette protection. Détruisez la protection en premier." 5 | line_number_out_of_bounds: "&4Saisir un numéro de ligne entre 2 et 4 (inclus)." 6 | no_permission: "&4Vous n'avez pas la permission d'utiliser cette commande." 7 | no_sign_selected: "&4Vous devez choisir un panneau sur un des block protégés d'abord en cliquant droit dessus." 8 | player_name_too_long: "&4Le nom du joueur dépasse les 16 caractères, cela dépasse le panneau. C'est d'ailleurs aussi plus long que la longueur conseillée par les créateurs de Minecraft !!" 9 | plugin_reloaded: "&6La Configuration du plugin a été rechargée !" 10 | sign_no_longer_part_of_protection: "&4Le panneau sélectionné n'est pas accolé à un block protégé." 11 | updated_sign: "&6Protection mise à jour !" 12 | protection: 13 | add_more_users_sign_instead: "&4Un seul panneau nommé [Privé] est possible, ajoutez un autre panneau nommé [Ainsi que]" 14 | bypassed: "&6Vous venez de passer outre une protection appartenant à {0}." 15 | cannot_change_sign: "&4Vous ne pouvez pas changer ce panneau, trop proche d'un block protégé." 16 | chest_hint: "&6Vous pouvez mettre un panneau sur ce coffre pour le protéger." 17 | claimed_container: "&6Block protégé. Personne d'autre ne peut y avoir accès." 18 | claimed_manually: "&6Protection créée. Seuls les personnes listées sur le panneau peuvent y accéder." 19 | expired: "&6La protection de ce block a expiré à cause de l'inactivité du propriétaire." 20 | in_wilderness: "&4Vous ne pouvez pas créer une protection ici: vous êtes dans une région sauvage !" 21 | is_claimed_by: "&6Ce block appartient à {0}." 22 | no_access: "&4Intéraction non autorisée, il appartient à {0}." 23 | no_permission_for_claim: "&4Vous n'avez pas la permission de protéger ce block, désolé !" 24 | not_nearby: "&4Aucun block protégeable à proximité. Faites (Shift-)Clic-Droit sur un block avec un panneau dans la main pour le protéger." 25 | selected_sign: "&6Protection sélectionnée: écrire \"/bl \" pour éditer cette protection." 26 | tag: 27 | everyone: ToutLeMonde 28 | more_users: 29 | - "[Ainsi que]" 30 | - "[More Users]" 31 | private: 32 | - "[Privé]" 33 | - "[Private]" 34 | redstone: Redstone 35 | timer: Timer 36 | updater: 37 | more_information: "&6Pour plus de détails: {0}" 38 | unsupported_server: "&4La dernière version de BlockLocker (version {0}) ne fonctionne pas avec cette version de Minecraft, seulement Minecraft {1}." 39 | update_available: "&6Une mise à jour de BlockLocker (version {0}) est disponible pour un téléchargement manuel" 40 | -------------------------------------------------------------------------------- /src/main/resources/translations-hu.yml: -------------------------------------------------------------------------------- 1 | command: 2 | cannot_be_used_by_console: Ezt a parancsot a konzol nem tudja használni. Sajnálom! 3 | cannot_edit_owner: 'A védelem tulajdonosát nem változtathatod meg. Inkább semmisítsd meg a táblát.' 4 | line_number_out_of_bounds: 'Kérlek, 2 és 4 közötti sorszámot adj meg.' 5 | no_permission: 'nincsjog' 6 | no_sign_selected: 'Először válassz ki egy olyan táblát jobb gombbal, ami egy védett blokkon van' 7 | player_name_too_long: 'A játékos neve több mint 16 karakterből áll, ami nem fér el a táblán. Hosszabb is, mint amennyit a Mojang lehetővé tesz a játékosok számára.' 8 | plugin_reloaded: 'A konfigurációs fájlok újratöltve!' 9 | sign_no_longer_part_of_protection: 'A kiválasztott tábla már nem tartozik védett blokkhoz.' 10 | updated_sign: 'A tábla frissítve!' 11 | protection: 12 | add_more_users_sign_instead: 'Csak egy [Privát] tábla lehet egy védett blokkon. További játékosokat ha szeretnél a védelemhez adni, akkor használd a [Felhasználók] feliratot a táblát.' 13 | bypassed: '{0} védelme adminisztrátori jogosulktságod miatt ignorálva.' 14 | cannot_change_sign: 'Levédet blokk közelében nem módosíthatsz táblákat.' 15 | chest_hint: 'Tegyél egy táblát erre a ládára, hogy megvédd azt.' 16 | claimed_container: 'Sikeresen levédve. Csak Te férsz hozzá mostantól.' 17 | claimed_manually: 'Sikeresen levédve. A táblán feltüntetett játékosok férnek csak hozzá.' 18 | expired: 'Ennek a blokknak a védelme a tulajdonos inaktivitása miatt lejárt.' 19 | in_wilderness: 'Itt nem hozhatsz létre védelmet.' 20 | is_claimed_by: 'A védelem tulajdonosa {0}.' 21 | no_access: 'Ezt nem használhatod. A védelem tulajdonosa {0}.' 22 | no_permission_for_claim: 'nincsjog' 23 | not_nearby: 'Nincs védett blokk a környékeden. Shift+jobb gomb egy tároló blokkra egy táblával a kezedben, hogy levédd azt.' 24 | selected_sign: 'Tábla kiválasztva. Használd a "/blocklocker " parancsot, hogy módosítsd a táblát. Ezeket is hozzáadhatod: [Vöröskő] vagy [Mindenki]' 25 | tag: 26 | everyone: Mindenki 27 | more_users: 28 | - '[Felhasználók]' 29 | - '[More Users]' 30 | private: 31 | - '[Privát]' 32 | - '[Private]' 33 | redstone: 34 | - Vöröskő 35 | - Redstone 36 | timer: 37 | - Időzítő 38 | - Timer 39 | updater: 40 | more_information: 'More information: {0}' 41 | unsupported_server: 'The latest version of BlockLocker (version {0}) no longer supports this Minecraft version, only Minecraft {1}.' 42 | update_available: 'An update for BlockLocker (version {0}) is available for manual download.' 43 | -------------------------------------------------------------------------------- /src/main/resources/translations-id.yml: -------------------------------------------------------------------------------- 1 | command: 2 | cannot_be_used_by_console: Perintah ini tidak dapat digunakan oleh konsol. Maaf! 3 | cannot_edit_owner: '&4Kamu tidak dapat mengganti pemilik proteksi. Hancurkan saja sign itu.' 4 | line_number_out_of_bounds: '&4Mohon masukkan nomor baris antara 2 dan 4, inklusif.' 5 | no_permission: '&4Kamu tidak punya izin untuk mengeksekusi perintah ini. Maaf!' 6 | no_sign_selected: '&4Kamu harus memilih sebuah sign di salah satu blok yang kamu lindungi dulu 7 | dengan mengeklik kanan itu.' 8 | player_name_too_long: '&4Nama pemain lebih dari 16 karakter, dimana tidak muat pada sign. Itu juga lebih panjang dari yang diperbolekan Mojang untuk nama pemain.' 9 | plugin_reloaded: '&6Memuat ulang file konfigurasi!' 10 | sign_no_longer_part_of_protection: '&4Sign yang dipilih tidak lagi menjadi milik blok yang dilindungi.' 11 | updated_sign: '&6Memperbarui sign!' 12 | protection: 13 | add_more_users_sign_instead: '&4Hanya boleh ada satu sign [Private] pada sebuah proteksi. Tambahkan saja sign [More Users].' 14 | bypassed: '&6Kamu baru saja melewati proteksi {0}.' 15 | cannot_change_sign: '&4Kamu tidak dapat mengubah sign di dekat kontainer yang sudah diklaim.' 16 | chest_hint: '&6Tempatkan sign pada chest ini untuk melindunginya.' 17 | claimed_container: '&6Mengklaim kontainer ini. Tidak ada orang lain yang dapat mengaksesnya.' 18 | claimed_manually: '&6Berhasil membuat klaim. Hanya orang tercantum pada sign yang dapat mengaksesnya.' 19 | expired: '&6Proteksi blok ini sudah kadaluwarsa karena pemiliknya tidak aktif.' 20 | in_wilderness: '&4Kamu tidak dapat membuat proteksi disini: kamu berada di hutan belantara.' 21 | is_claimed_by: '&6Blok ini sudah diklaim oleh {0}.' 22 | no_access: '&4Kamu tidak dapat menggunakan blok ini, itu dilidungi oleh {0}.' 23 | no_permission_for_claim: '&4Kamu tidak punya izin untuk mengklaim kontainer, maaf.' 24 | not_nearby: '&4Tidak ada block yang dapat dilindungi di dekat sini. (Shift-)klik-kanan kontainer 25 | dengan sign di tangan mu untuk melindunginya.' 26 | tag: 27 | everyone: Everyone 28 | more_users: ['[More Users]', '[User Lain]'] 29 | private: ['[Private]', '[Pribadi]'] 30 | redstone: Redstone 31 | timer: Timer 32 | updater: 33 | more_information: '&6Informasi lebih lanjut: {0}' 34 | unsupported_server: '&4Versi terbaru BlockLocker (versi {0}) tidak lagi mendukung versi Minecraft ini, hanya Minecraft {1}.' 35 | update_available: '&6Pembaruan untuk BlockLocker (versi {0}) tersedia untuk diunduh secara manual.' 36 | -------------------------------------------------------------------------------- /src/main/resources/translations-it.yml: -------------------------------------------------------------------------------- 1 | # Translations file for the Italian language 2 | command: 3 | cannot_be_used_by_console: "Questo comando non può essere usato dalla console. Spiacente!" 4 | cannot_edit_owner: "&4Non puoi cambiare il proprietario della protezione. Distruggi invece il cartello" 5 | line_number_out_of_bounds: "&4Per favore, utilizza un numero di linea compreso tra 2 e 4 inclusi." 6 | no_sign_selected: "&4Devi selezionare un cartello su uno dei tuoi blocchi protetti cliccando con il tasto destro su di esso." 7 | no_permission: "&4Non hai il permesso di eseguire questo comando. Spiacente!" 8 | player_name_too_long: "&4Il nome del giocatore è di oltre 16 caratteri, ciò non permette di essere inserito sul cartello. Inoltre è più lungo dei caratteri massimi del nome concessi da Mojang." 9 | plugin_reloaded: "&6Ricaricati i file di configurazione con successo!" 10 | sign_no_longer_part_of_protection: "&4Il cartello selezionato non appartiene più ad un blocco protetto." 11 | updated_sign: "&6Aggiornato il cartello!" 12 | protection: 13 | add_more_users_sign_instead: "&4Ci può essere solo una etichetta [Privato] su una protezione. Aggiungi invece l'etichetta [Più utenti]." 14 | bypassed: "&6Hai appena ignorato una protezione di {0}." 15 | can_only_add_protection_sign: "&4CI può essere solo un etichetta [Privato] e zero or più [More Users] su un cartello. Altre etichette non sono permesse." 16 | cannot_change_sign: "&4Non puoi cambiare un cartello vicino ad un contenitore reclamato." 17 | chest_hint: "&6Posiziona un cartello su questo baula per proteggerlo." 18 | claimed_container: "&6Reclamato questo contenitore. Nessun altro può accedervi." 19 | claimed_manually: "&6Reclamato con successo. Solo le persone elencate nel cartello possono accedervi." 20 | expired: "&6La protezione di questo blocco è scaduta data l'inattività del proprietario." 21 | in_wilderness: "&4Non puoi creare una protezione qui: sei in una area selvaggia." 22 | is_claimed_by: "&6Questo blocco è stato reclamato da {0}." 23 | no_access: "&4Non puoi usare questo blocco, è protetto da {0}." 24 | no_permission_for_claim: "&4Non hai il permesso di reclamare questo contenitore, spiacente." 25 | not_nearby: "&4Non è presente alcun blocco da proteggere. Utilizza (Shift-)tasto destro su un contenitore con un cartello in mano per proteggerlo." 26 | tag: 27 | everyone: "Chiunque" 28 | timer: "Timer" 29 | redstone: "Redstone" 30 | private: "[Privato]" 31 | more_users: "[Più utenti]" 32 | updater: 33 | more_information: "&6Maggiori informazioni: {0}" 34 | unsupported_server: "&4L'ultima versione di ${project.name} (version {0}) non supporta più questa versione di Minecraft, solo Minecraft {1} è supportato." 35 | update_available: "&6Un aggiornamento per ${project.name} (version {0}) è disponibile per il download manuale." 36 | updated_automatically: "&6Un aggiornamento per ${project.name} è disponibile (version {0}). Sarà automaticamente installato al prossimo riavvio del server." 37 | -------------------------------------------------------------------------------- /src/main/resources/translations-ja.yml: -------------------------------------------------------------------------------- 1 | # Translations file for the English language 2 | command: 3 | cannot_be_used_by_console: "申し訳ありません。このコマンドはコンソールから実行することはできません。" 4 | cannot_edit_owner: "&4保護の所有者を変更できません。代わりに看板を破壊してください。" 5 | line_number_out_of_bounds: "&42から4までの行番号を入力してください。" 6 | no_sign_selected: "&4最初に、保護されたブロックの1つを右クリックしてからそのサインを選択する必要があります。" 7 | no_permission: "&4このコマンドを実行する権限がありません!" 8 | player_name_too_long: "&4プレイヤー名が16文字を超えているため看板に収まりません。また、Mojangが許可するプレイヤー名の長さよりも長くなります。" 9 | plugin_reloaded: "&6構成ファイルを再読み込みしました" 10 | sign_no_longer_part_of_protection: "&4選択された看板は保護されたブロックに付属していません。" 11 | updated_sign: "&6看板を更新しました!" 12 | protection: 13 | add_more_users_sign_instead: "&4保護には[プライベート]サインが1つだけ存在できます。代わりに[追加ユーザー]サインを追加してください。" 14 | bypassed: "&6あなたは{0}の保護をバイパスしました。" 15 | can_only_add_protection_sign: "&4保護には、[プライベート]のサインが1つだけで、[追加ユーザー]のサインが0個以上ある場合があります。 他の標識は許可されていません。" 16 | cannot_change_sign: "&4他人の保護されたブロックの看板は編集できません。" 17 | chest_hint: "&6このチェストに看板を設置することで保護することができます!" 18 | claimed_container: "&6このブロックを保護しました。誰もこれにアクセスすることはできません。" 19 | claimed_manually: "&6保護を作成しました。看板に載っているプレイヤーのみがアクセスできます。" 20 | expired: "&6このブロックの保護は、所有者が非アクティブであるために期限切れになりました。" 21 | in_wilderness: "&4ここで保護を作成することはできません。あなたは非管理区域にいます。" 22 | is_claimed_by: "&6このブロックはすでに {0} によって保護されています。" 23 | no_access: "&4このブロックは {0} によって保護されているためアクセスできません。" 24 | no_permission_for_claim: "&4このブロックを保護する権限がありません。" 25 | not_nearby: "&4近くに保護できるブロックがありません。看板を持ってしゃがみながらそのブロックに向けて右クリックしてください。" 26 | selected_sign: "&6看板を選択しました。\"/blocklocker <行番号> <名前>\"を実行することで編集できます。もしくは&n[レッドストーン]&r&6 &n[全員]&r&6を追加してください。" 27 | tag: 28 | everyone: "全員" 29 | timer: "タイマー" 30 | redstone: "レッドストーン" 31 | private: "[プライベート]" 32 | more_users: "[追加ユーザー]" 33 | updater: 34 | more_information: "&6追加の情報: {0}" 35 | unsupported_server: "&4最新のバージョンは${project.name} (バージョン {0}) です。このマインクラフトのバージョンはサポートされていません。マインクラフト {1} を使用してください" 36 | update_available: "&6新しいバージョン ${project.name} (バージョン {0}) があります。手動でダウンロードしてください。" 37 | updated_automatically: "&6新しいバージョン ${project.name}(バージョン {0}) があります。再起動時に自動的にダウンロードされます" 38 | -------------------------------------------------------------------------------- /src/main/resources/translations-kr.yml: -------------------------------------------------------------------------------- 1 | command: 2 | cannot_be_used_by_console: "이 명령은 콘솔에서 사용할 수 없습니다. 죄송해요!" 3 | cannot_edit_owner: "&4이 보호의 권한 소유자를 변경할 수 없습니다. 대신 표지판을 제거하십시오." 4 | line_number_out_of_bounds: "&42 이상, 4 이하의 줄 번호를 입력하십시오." 5 | no_sign_selected: "&4우선 마우스 오른쪽 버튼을 클릭하여, 보호받는 블럭의 표지판을 선택해야 합니다." 6 | no_permission: "&4당신은 이 명령을 실행할 수 있는 권한이 없습니다. 죄송해요!" 7 | player_name_too_long: "&4플레이어의 이름이 16자를 넘어 표지판에 적을 수 없습니다. 뿐만 아니라 Mojang이 허용하고 있는 플레이어 이름의 길이보다 깁니다." 8 | plugin_reloaded: "&6구성 파일을 다시 로드했습니다!" 9 | sign_no_longer_part_of_protection: "&4선택한 표지판이 더 이상 보호받는 블럭에 있지 않습니다." 10 | updated_sign: "&6표지판을 업데이트했습니다!" 11 | protection: 12 | add_more_users_sign_instead: "&4한 보호 설정에는 하나의 [Private] 표지판만 설치 가능합니다. 대신 [More Users] 표지판을 추가하세요." 13 | bypassed: "&6방금 {0}의 보호를 무시하였습니다." 14 | can_only_add_protection_sign: "&4한 보호 설정에는 하나의 [Private] 표지판과 0개 또는 그 이상의 [More Users] 표지판만 설치 가능합니다. 다른 표지판은 허용되지 않습니다." 15 | cannot_change_sign: "&4보호 설정된 보관함의 표지판을 변경할 수 없습니다." 16 | chest_hint: "&6상자에 표지판을 설치해 보호하세요." 17 | claimed_container: "&6보관함을 보호 설정했습니다. 다른 누구도 이 보관함을 사용할 수 없습니다." 18 | claimed_manually: "&6보호 설정되었습니다. 표지판에 적힌 플레이어만 이 블럭을 사용할 수 있습니다." 19 | expired: "&6소유자가 비활성함에 따라 이 블록에 대한 보호는 만료되었습니다." 20 | in_wilderness: "&4여기에서 보호 설정을 할 수 없습니다: 당신은 황야에 있습니다." 21 | is_claimed_by: "&6이 블록은 {0}의 소유입니다." 22 | no_access: "&4이 블록은 {0}에게 소유되어있어서 당신이 사용할 수 없습니다." 23 | no_permission_for_claim: "&4당신은 보관함을 보호 설정할 권한이 없습니다. 죄송합니다." 24 | not_nearby: "&4가까이에 보호할 수 있는 블럭이 없습니다. 표지판을 손에 들고 웅크린 상태에서 보관함에 마우스 오른쪽 버튼을 클릭하여 보호 설정합니다." 25 | selected_sign: "&6표지판을 선택했습니다. 이 표지판을 수정하려면 채팅창에 \"/blocklocker <줄 번호> <닉네임>\"을 입력합니다. 또는 이들을 추가합니다: &n[Redstone]&r&6 &n[Everyone]&r&6.}" 26 | tag: 27 | everyone: "Everyone" 28 | timer: "Timer" 29 | redstone: "Redstone" 30 | private: "[Private]" 31 | more_users: "[More Users]" 32 | updater: 33 | more_information: "&6추가 정보: {0}" 34 | unsupported_server: "&4${project.name} (version {0})의 최신 버전이 이 Minecraft 버전을 지원하지 않습니다. (Minecraft {1}만)." 35 | update_available: "&6${project.name} (version {0})에 대한 업데이트를 수동으로 다운로드할 수 있습니다." 36 | updated_automatically: "&6${project.name}에 대한 업데이트가 가능합니다(version {0}). 서버를 다시 시작하면 자동으로 설치됩니다." -------------------------------------------------------------------------------- /src/main/resources/translations-nl.yml: -------------------------------------------------------------------------------- 1 | # Translation file for the Dutch language 2 | command: 3 | cannot_be_used_by_console: 'Dit commando kan niet door de console gebruikt worden. Sorry!' 4 | cannot_edit_owner: '&4Je kunt niet aanpassen wie de eigenaar is van de bescherming. Je kunt wel de bescherming slopen.' 5 | line_number_out_of_bounds: '&4Gebruik alsjeblieft een regelnummer van 2 t/m 4.' 6 | no_permission: '&4Je hebt geen toestemming om dit commando te gebruiken. Sorry!' 7 | no_sign_selected: '&4Selecteer eerst een bordje door te rechtklikken.' 8 | player_name_too_long: '&4De spelernaam is langer dan 16 tekens. Dit gaat niet passen. Mojang staat ook niet toe dat spelernamen langer dan 16 tekens zijn.' 9 | plugin_reloaded: '&6Configuratiebestanden zijn opnieuw geladen!' 10 | sign_no_longer_part_of_protection: '&4Het eerder geselecteerde bordje hoort niet langer bij een bescherming.' 11 | updated_sign: '&6Bordje is bijgewerkt!' 12 | protection: 13 | add_more_users_sign_instead: '&4Je mag maar één bordje met [Op slot] ophangen per bescherming. Gebruik in plaats daarvan bordjes met [More Users]..' 14 | bypassed: '&6Je hebt ingebroken bij een bescherming van {0}.' 15 | cannot_change_sign: '&4Je kunt geen bordje aanpassen aan een beschermd blok.' 16 | chest_hint: '&6Plaats een bordje tegen deze kist om de kist te beschermen.' 17 | claimed_container: '&6Deze container is nu beschermd. Alleen jij kan er nog bij.' 18 | claimed_manually: '&6Je hebt een beschermingsbordje opgehangen. Alleen spelers die staan op het bordje kunnen er nog bij.' 19 | expired: '&6The protection of this block has expired due to inactivity of the owner.' 20 | in_wilderness: '&4You cannot create a protection here: you are in the wilderness.' 21 | is_claimed_by: '&6Dit blok is beschermd door {0}.' 22 | no_access: '&4Dit blok is beschermd door {0}, je kunt er niet bij.' 23 | no_permission_for_claim: '&4Je hebt geen toestemming om beschermingsbordjes op te hangen, sorry.' 24 | not_nearby: '&4Er is hier geen blok dat beschermd kan worden. (Shift-)rechtsklik een containerblok met een bordje in je hand om het te beschermen.' 25 | tag: 26 | everyone: Everyone 27 | more_users: 28 | - '[Meer Spelers]' 29 | - '[More Users]' 30 | private: 31 | - '[Op slot]' 32 | - '[Private]' 33 | redstone: Redstone 34 | timer: Timer 35 | updater: 36 | more_information: '&6Meer informatie: {0}' 37 | unsupported_server: '&4De nieuwste versie van BlockLocker (versie {0}) ondersteunt niet langer deze Minecraftversie, maar slechts nog Minecraft {1}.' 38 | update_available: '&6Een bijgewerkte versie van BlockLocker (versie {0}) is kan handmatig gedownload worden.' 39 | -------------------------------------------------------------------------------- /src/main/resources/translations-pl.yml: -------------------------------------------------------------------------------- 1 | command: 2 | cannot_be_used_by_console: Ta komenda nie może być użyta przez konsolę. Przepraszam! 3 | cannot_edit_owner: '&4Nie możesz zmienić właściciela tego zabezpieczenia. Zniszcz tabliczkę.' 4 | line_number_out_of_bounds: '&4Proszę wpisać numer wiersza z przedziału od 2 do 4 włącznie.' 5 | no_permission: '&4Nie masz uprawnień do wykonania tej komendy. Przepraszam!' 6 | no_sign_selected: '&4Musisz najpierw wybrać tabliczke na jednym z chronionych bloków klikając go prawym przyciskiem myszy.' 7 | player_name_too_long: '&4Nazwa gracza ma ponad 16 znaków, co nie mieści się na znaku. Jest też dłuższa niż zezwala na to Mojang.' 8 | plugin_reloaded: '&6Załadowano ponownie pliki konfiguracyjne!' 9 | sign_no_longer_part_of_protection: '&4Wybrana tabliczka nie należy już do chronionego bloku.' 10 | updated_sign: '&6Zaktualizowano tabliczkę!' 11 | protection: 12 | add_more_users_sign_instead: '&4Na danym zabezpieczeniu może być tylko jedna tabliczka [Prywatne]. Zamiast tego dodaj tabliczkę [Wiecej graczy].' 13 | bypassed: '&6Właśnie ominąłeś ochronę {0}.' 14 | cannot_change_sign: '&4Nie można zmienić tabliczki w pobliżu zajętego obiektu.' 15 | chest_hint: '&6Postaw tabliczkę na tej skrzyni, aby ją chronić.' 16 | claimed_container: '&6Zabezpieczono ten obiekt. Nikt inny nie ma do niego dostępu.' 17 | claimed_manually: '&6Stworzono zabezpieczenie. Dostęp do niego mają tylko osoby wymienione na tabliczce.' 18 | expired: '&6Ochrona tego bloku wygasła z powodu braku aktywności właściciela.' 19 | in_wilderness: '&4Nie możesz tu stworzyć ochrony: jesteś na pustkowiu.' 20 | is_claimed_by: '&6Ten blok został zajęty przez {0}.' 21 | no_access: '&4Nie możesz użyć tego bloku, jest on chroniony przez {0}.' 22 | no_permission_for_claim: '&4Nie masz uprawnień do zajęcia tego obiektu, przykro mi.' 23 | not_nearby: '&4W pobliżu nie ma bloku, który można ochronić. (Shift-)kliknij prawym przyciskiem myszy na obiekt z tabliczką w dłoni, aby go ochronić.' 24 | selected_sign: '&6Wybrano tabliczkę. Wpisz "/blocklocker ", aby edytować tą tabliczkę. Ewentualnie dodaj poniższe: &n[Redstone]&r&6 &n[Wszyscy]&r&6.}' 25 | tag: 26 | everyone: Wszyscy 27 | more_users: '[Wiecej graczy]' 28 | private: '[Prywatne]' 29 | redstone: Redstone 30 | timer: Licznik 31 | updater: 32 | more_information: '&6Więcej informacji: {0}' 33 | unsupported_server: '&4Najnowsza wersja BlockLockera (wersja {0}) już nie obsługuje tej wersji Minecrafta, tylko Minecrafta {1}.' 34 | update_available: '&6Aktualizacja dla pluginu BlockLocker (wersja {0}) jest dostępna do ręcznego pobrania.' -------------------------------------------------------------------------------- /src/main/resources/translations-tr.yml: -------------------------------------------------------------------------------- 1 | command: 2 | cannot_be_used_by_console: "Bu komut konsol tarafından kullanılamaz. Üzgünüm!" 3 | cannot_edit_owner: "&4Korumanın sahibini değiştiremezsiniz. Bunun yerine tabelayı kırın." 4 | line_number_out_of_bounds: "&4Lütfen 2 ile 4 arasında bir satır numarası girin, dahil." 5 | no_sign_selected: "&4Önce korunan bloklardan birindeki tabelaya sağ tıklayarak seçmelisiniz." 6 | no_permission: "&4Bu komutu çalıştırmak için izniniz yok. Üzgünüm!" 7 | player_name_too_long: "&4Oyuncu adı 16 karakterden fazla, bu da tabelaya sığmıyor. Ayrıca, Mojang'ın izin verdiği oyuncu adlarından daha uzun." 8 | plugin_reloaded: "&6Yapılandırma dosyaları yeniden yüklendi!" 9 | sign_no_longer_part_of_protection: "&4Seçilen tabela artık korunan bir bloğa ait değil." 10 | updated_sign: "&6Tabela güncellendi!" 11 | protection: 12 | add_more_users_sign_instead: "&4Bir korumada yalnızca bir [Özel] tabelası olabilir. Bunun yerine bir [Ekstra Kullanıcı] tabelası ekleyin." 13 | bypassed: "&6{0} adlı korumayı atlattınız." 14 | can_only_add_protection_sign: "&4Bir korumada yalnızca bir [Özel] ve sıfır veya daha fazla [Ekstra Kullanıcı] tabelası olabilir. Diğer tabelalara izin verilmez." 15 | cannot_change_sign: "&4Bir talep edilen konteynerın yakınındaki tabelayı değiştiremezsiniz." 16 | chest_hint: "&6Bu sandığın üzerine tabela koyarak koruyun." 17 | claimed_container: "&6Bu konteynerı talep ettiniz. Başka kimse erişemez." 18 | claimed_manually: "&6Bir talep oluşturuldu. Sadece tabelada listelenen kişiler erişebilir." 19 | expired: "&6Bu bloğun koruması sahibin etkin olmaması nedeniyle süresi doldu." 20 | in_wilderness: "&4Burada bir koruma oluşturamazsınız: vahşi alandasınız." 21 | is_claimed_by: "&6Bu blok {0} tarafından talep edildi." 22 | no_access: "&4Bu bloğu kullanamazsınız, {0} tarafından korunuyor." 23 | no_permission_for_claim: "&4Bir konteynerı talep etme izniniz yok, üzgünüm." 24 | not_nearby: "&4Yakınında korunabilir bir blok yok. Bir tabela taşıyarak bir konteynera (Shift-)sağ tıklayın." 25 | tag: 26 | everyone: "Herkes" 27 | timer: "Zamanlayıcı" 28 | redstone: "Kızıltaş" 29 | private: "[Özel]" 30 | more_users: "[Ekstra Kullanıcı]" 31 | updater: 32 | more_information: "&6Daha fazla bilgi: {0}" 33 | unsupported_server: "&4${project.name} 'ın en son sürümü ({0} sürümü) artık bu Minecraft sürümünü desteklemiyor, sadece Minecraft {1}." 34 | update_available: "&6${project.name} için bir güncelleme (sürüm {0}) manuel indirme için mevcut." 35 | updated_automatically: "&6${project.name} için bir güncelleme mevcut (sürüm {0}). Sunucuyu yeniden başlattığınızda otomatik olarak yüklenecek." 36 | -------------------------------------------------------------------------------- /src/main/resources/translations-vi.yml: -------------------------------------------------------------------------------- 1 | command: 2 | cannot_be_used_by_console: Xin lỗi!, câu lệnh này không thể sử dụng trong bảng điều khiển. 3 | cannot_edit_owner: '&4Bạn không thể thay đổi chủ sở hữu của khối được bảo vệ. Thay vào đó hãy phá hủy cái báng' 4 | line_number_out_of_bounds: '&4Xin hãy nhập số dòng giữa 2 và 4.' 5 | no_permission: '&4Xin lỗi, nhưng bạn không có quyền để thực hiên câu lệnh này.' 6 | no_sign_selected: '&4Bạn phải chọn một cái bảng trên một khối mà bạn đã bảo vệ trước bằng cách đúp chuột phải vào nó.' 7 | player_name_too_long: '&4Tên người chơi dài hơn 16 ký tự, nó không đủ chỗ ở trên bảng. Tên này cũng dài hơn giới hạn cho phép của Mojang đối với tên người chơi.' 8 | plugin_reloaded: '&6Đã tải lại tệp cài đặt' 9 | sign_no_longer_part_of_protection: '&4Bảng đã chọn không còn thuộc về khối được bảo vệ nữa.' 10 | updated_sign: '&6Đã cập nhật bảng.' 11 | protection: 12 | add_more_users_sign_instead: '&4Chỉ có thể có một bảng [Riêng tư]. Thêm bảng [Thêm Người Chơi] để thêm nhiều người hơn.' 13 | bypassed: '&6Bạn đã bỏ qua các hạn chế truy cập của {0}.' 14 | cannot_change_sign: '&4Bạn không thể thay đổi một cái bảng gần một thùng chứa đồ đã có chủ sở hữu.' 15 | chest_hint: '&6Đặt một cái bảng trên cái rương này để bảo vệ nó.' 16 | claimed_container: '&6Thùng chứa đồ này đã được bảo vệ. Không ai có thể mở được nó.' 17 | claimed_manually: '&6Thúng chứa đồ đã được bảo vệ. Chỉ có những người được liệt kê mới mở được nó.' 18 | expired: '&6Sự bảo vệ của khối này đã hết hạn vì chủ sỡ hữu của nó đã không hoạt động trong một thời gian dài.' 19 | in_wilderness: '&4Bạn không thể bảo vệ khối này: bạn đang ở vùng hoang dã.' 20 | is_claimed_by: '&6Khối này đã được sở hữu bởi {0}.' 21 | no_access: '&4Bạn không thể sử dụng khối này, nó đang được bảo vệ bởi {0}.' 22 | no_permission_for_claim: '&4Xin lỗi, bạn không có quyền sở hữu thùng chứa dồ.' 23 | not_nearby: '&4Không có khối nào ở gần có thể được bảo vệ. (Shift-)chuột phải vào một thùng chứa đồ với một cái bảng ở trên tay để bảo vệ nó.' 24 | tag: 25 | everyone: Mọi người 26 | more_users: '[Thêm Người Chơi]' 27 | private: '[Riêng Tư]' 28 | redstone: Đá đỏ 29 | timer: Thời gian 30 | updater: 31 | more_information: '&6Thông tin thêm: {0}' 32 | unsupported_server: '&4Phiên bản mới nhất của BlockLocker (version {0}) Không còn hỗ trợ phiên bản Minecraft này nữa, chỉ hỗ trợ Minecraft phiên bản {1}.' 33 | update_available: '&6Một phiên của BlockLocker (version {0}) đã có sẵn để tải thủ công.' 34 | -------------------------------------------------------------------------------- /src/main/resources/translations-zh-hk.yml: -------------------------------------------------------------------------------- 1 | # Translation file for the Hong Kong Traditional Chinese language. Translations by flandretw. 2 | command: 3 | cannot_be_used_by_console: '抱歉!此指令無法在控制台使用。' 4 | cannot_edit_owner: '&4你不能變更保護的擁有者。而是只能破壞掉這個告示牌。' 5 | line_number_out_of_bounds: '&4請輸入 2~4 之間的行數(包括 2 和 4)。' 6 | no_permission: '&4抱歉!你沒有執行此指令的權限。' 7 | no_sign_selected: '&4必須先對受保護方塊的告示牌點擊右鍵選擇。' 8 | player_name_too_long: '&4玩家名稱超過 16 個字元,告示牌無法容納。它也比 Mojang 允許的玩家名稱還要長。' 9 | plugin_reloaded: '&6已重新載入設定檔!' 10 | sign_no_longer_part_of_protection: '&4選擇的告示牌不再屬於被保護的方塊。' 11 | updated_sign: '&6已更新告示牌!' 12 | protection: 13 | add_more_users_sign_instead: '&4只能有一個 [私人] 告示牌。新增 [更多] 告示牌來新增更多玩家。' 14 | bypassed: '&6你剛剛繞過了 {0} 的保護。' 15 | cannot_change_sign: '&4你不能編輯已認領容器的告示牌。' 16 | chest_hint: '&6手持告示牌,並對儲物箱點擊右鍵來認領它。' 17 | claimed_container: '&6已成功認領此容器。其他人將無法使用。' 18 | claimed_manually: '&6已成功認領。只有清單上的人才能存取。' 19 | expired: '&6由於此方塊的擁有者不活躍,保護已失效。' 20 | in_wilderness: '&4你不能在這裏建立保護。' 21 | is_claimed_by: '&6這個方塊屬於 {0}。' 22 | no_access: '&4你不能使用這個方塊,它屬於 {0}。' 23 | no_permission_for_claim: '&4抱歉。你沒有認領容器的權限。' 24 | not_nearby: '&4附近沒有可被保護的方塊。手持告示牌按住 Shift 鍵,並對容器點擊右鍵來保護它。' 25 | tag: 26 | everyone: 27 | - '所有人' 28 | - 'Everyone' 29 | timer: 30 | - '計時' 31 | - 'Timer' 32 | redstone: 33 | - '紅石' 34 | - 'Redstone' 35 | private: 36 | - '[私人]' 37 | - '[Private]' 38 | more_users: 39 | - '[更多]' 40 | - '[More Users]' 41 | updater: 42 | more_information: '&6更多資訊:{0}' 43 | unsupported_server: '&4最新版本的 BlockLocker(版本 {0})不再支援此 Minecraft 版本,需要使用 Minecraft {1}。' 44 | update_available: '&6新版本的 BlockLocker(版本 {0})已可用,需要手動下載。' 45 | -------------------------------------------------------------------------------- /src/main/resources/translations-zh-tw.yml: -------------------------------------------------------------------------------- 1 | # Translation file for the Taiwan Traditional Chinese language. Translations by flandretw. 2 | command: 3 | cannot_be_used_by_console: '抱歉!此指令無法在控制台使用。' 4 | cannot_edit_owner: '&4你不能變更保護的擁有者。而是只能破壞掉這個告示牌。' 5 | line_number_out_of_bounds: '&4請輸入 2~4 之間的行數(包括 2 和 4)。' 6 | no_permission: '&4抱歉!你沒有執行此指令的權限。' 7 | no_sign_selected: '&4必須先對受保護方塊的告示牌點擊右鍵選擇。' 8 | player_name_too_long: '&4玩家名稱超過 16 個字元,告示牌無法容納。它也比 Mojang 允許的玩家名稱還要長。' 9 | plugin_reloaded: '&6已重新載入設定檔!' 10 | sign_no_longer_part_of_protection: '&4選擇的告示牌不再屬於被保護的方塊。' 11 | updated_sign: '&6已更新告示牌!' 12 | protection: 13 | add_more_users_sign_instead: '&4只能有一個 [私人] 告示牌。新增 [更多] 告示牌來新增更多玩家。' 14 | bypassed: '&6你剛剛繞過了 {0} 的保護。' 15 | cannot_change_sign: '&4你不能編輯已認領容器的告示牌。' 16 | chest_hint: '&6手持告示牌,並對儲物箱點擊右鍵來認領它。' 17 | claimed_container: '&6已成功認領此容器。其他人將無法使用。' 18 | claimed_manually: '&6已成功認領。只有清單上的人才能存取。' 19 | expired: '&6由於此方塊的擁有者不活躍,保護已失效。' 20 | in_wilderness: '&4你不能在這裡建立保護。' 21 | is_claimed_by: '&6這個方塊屬於 {0}。' 22 | no_access: '&4你不能使用這個方塊,它屬於 {0}。' 23 | no_permission_for_claim: '&4抱歉。你沒有認領容器的權限。' 24 | not_nearby: '&4附近沒有可被保護的方塊。手持告示牌按住 Shift 鍵,並對容器點擊右鍵來保護它。' 25 | tag: 26 | everyone: 27 | - '所有人' 28 | - 'Everyone' 29 | timer: 30 | - '計時' 31 | - 'Timer' 32 | redstone: 33 | - '紅石' 34 | - 'Redstone' 35 | private: 36 | - '[私人]' 37 | - '[私有]' 38 | - '[Private]' 39 | more_users: 40 | - '[更多]' 41 | - '[More Users]' 42 | updater: 43 | more_information: '&6更多資訊:{0}' 44 | unsupported_server: '&4最新版本的 BlockLocker(版本 {0})不再支援此 Minecraft 版本,需要使用 Minecraft {1}。' 45 | update_available: '&6新版本的 BlockLocker(版本 {0})已可用,需要手動下載。' 46 | -------------------------------------------------------------------------------- /src/main/resources/translations-zh.yml: -------------------------------------------------------------------------------- 1 | # Translation file for the Simplified Chinese language. Translations by Halogly. 2 | command: 3 | cannot_be_used_by_console: "该命令无法通过控制台输出." 4 | cannot_edit_owner: "&4你不能编辑使用者." 5 | line_number_out_of_bounds: "&4请输入2~4之间的行数(包括2和4)." 6 | no_sign_selected: "&4你需要右键来选择告示牌." 7 | no_permission: "&4你没有权限." 8 | player_name_too_long: "&4玩家名称过长." 9 | plugin_reloaded: "&6已重新加载配置文件!" 10 | sign_no_longer_part_of_protection: "&4选择的告示牌不再属于被保护的方块." 11 | updated_sign: "&6已更新告示牌!" 12 | protection: 13 | add_more_users_sign_instead: "&4只能有一个 [私有] 标签. 添加 [更多] 标签来添加更多人." 14 | bypassed: "&6你绕过了 {0} 的访问限制." 15 | can_only_add_protection_sign: "&4只能有一个 [私有] 标签和零或多个 [更多] 标签. 不允许其他标签." 16 | cannot_change_sign: "&4你不能编辑." 17 | chest_hint: "&6手持告示牌并右键箱子来锁定箱子." 18 | claimed_container: "&6已成功上锁. 其他人将无法使用." 19 | claimed_manually: "&6已成功上锁. 只有名单上的人才能访问." 20 | expired: "&6由于该方块的拥有者不活跃, 保护已失效." 21 | in_wilderness: "&4你不能在这里创建保护." 22 | is_claimed_by: "&6这个方块属于 {0}." 23 | no_access: "&4你不能使用这个方块, 它属于 {0}." 24 | no_permission_for_claim: "&4你没有权限." 25 | not_nearby: "&4附近没有可被保护的方块, 手持告示牌按住Shift并右键容器来保护它." 26 | tag: 27 | everyone: 28 | - "所有人" 29 | - "Everyone" 30 | timer: 31 | - "定时" 32 | - "Timer" 33 | redstone: 34 | - "红石" 35 | - "Redstone" 36 | private: 37 | - "[私有]" 38 | - "[Private]" 39 | more_users: 40 | - "[更多]" 41 | - "[More Users]" 42 | updater: 43 | more_information: "&6更多信息: {0}" 44 | unsupported_server: "&4最新版本的 ${project.name} (version {0}) 不再支持这个Minecraft版本, 需要Minecraft {1} 版本." 45 | update_available: "&6新版本 ${project.name} (version {0}) 已经可用, 需要手动下载." 46 | updated_automatically: "&6${project.name} 有可用更新 (version {0}), 重启服务器以自动更新." -------------------------------------------------------------------------------- /src/test/java/nl/rutgerkok/blocklocker/TestPlayer.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker; 2 | 3 | import java.util.Locale; 4 | import java.util.UUID; 5 | 6 | import org.bukkit.entity.Player; 7 | import org.mockito.Mockito; 8 | 9 | import com.google.common.base.Charsets; 10 | 11 | public class TestPlayer { 12 | 13 | public static Player create() { 14 | return create("TestPlayer"); 15 | } 16 | 17 | public static Player create(String name) { 18 | UUID uuid = UUID.nameUUIDFromBytes(name.toLowerCase(Locale.ROOT).getBytes(Charsets.UTF_8)); 19 | return create(name, uuid); 20 | } 21 | 22 | public static Player create(String name, UUID uuid) { 23 | Player player = Mockito.mock(Player.class); 24 | Mockito.when(player.getName()).thenReturn(name); 25 | Mockito.when(player.getUniqueId()).thenReturn(uuid); 26 | return player; 27 | } 28 | } -------------------------------------------------------------------------------- /src/test/java/nl/rutgerkok/blocklocker/group/CombinedGroupSystemTest.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.group; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertFalse; 5 | import static org.junit.jupiter.api.Assertions.assertTrue; 6 | 7 | import java.util.Arrays; 8 | import java.util.Collection; 9 | 10 | import org.bukkit.entity.Player; 11 | import org.junit.jupiter.api.Assertions; 12 | import org.junit.jupiter.api.Test; 13 | 14 | import nl.rutgerkok.blocklocker.TestPlayer; 15 | 16 | public class CombinedGroupSystemTest { 17 | 18 | private class ReturnsFalseGroupSystem extends GroupSystem { 19 | 20 | @Override 21 | public boolean isInGroup(Player player, String groupName) { 22 | return false; 23 | } 24 | 25 | @Override 26 | public boolean keepOnReload() { 27 | return false; 28 | } 29 | 30 | } 31 | 32 | private class ReturnsTrueGroupSystem extends GroupSystem { 33 | 34 | @Override 35 | public boolean isInGroup(Player player, String groupName) { 36 | return true; 37 | } 38 | 39 | @Override 40 | public boolean keepOnReload() { 41 | return true; 42 | } 43 | 44 | } 45 | 46 | @Test 47 | public void testAddNullSystem() { 48 | // Null systems are not allowed 49 | Assertions.assertThrows(NullPointerException.class, () -> { 50 | new CombinedGroupSystem().addSystem(null); 51 | }); 52 | } 53 | 54 | @SuppressWarnings("deprecation") 55 | @Test 56 | public void testAddNullSystems() { 57 | Assertions.assertThrows(NullPointerException.class, () -> { 58 | // Collections containing null systems are not allowed 59 | Collection containingNull = Arrays.asList( 60 | new ReturnsFalseGroupSystem(), 61 | new ReturnsTrueGroupSystem(), 62 | null); 63 | new CombinedGroupSystem().addSystems(containingNull); 64 | }); 65 | } 66 | 67 | @Test 68 | public void testCombined() { 69 | CombinedGroupSystem combinedGroupSystem = new CombinedGroupSystem(); 70 | combinedGroupSystem.addSystem(new ReturnsFalseGroupSystem()); 71 | combinedGroupSystem.addSystem(new ReturnsTrueGroupSystem()); 72 | 73 | // Must return true, because ReturnsTrueGroupSystem returns true 74 | assertTrue(combinedGroupSystem.isInGroup(TestPlayer.create(), "GroupName")); 75 | } 76 | 77 | @Test 78 | public void testEmptyGroupSystem() { 79 | assertFalse(new CombinedGroupSystem().isInGroup(TestPlayer.create(), "GroupName")); 80 | } 81 | 82 | @Test 83 | public void testReloadSurvivors() { 84 | GroupSystem keptOnReload = new ReturnsTrueGroupSystem(); 85 | GroupSystem removedOnReload = new ReturnsFalseGroupSystem(); 86 | 87 | // Add two systems, one surviving the reload, one not 88 | CombinedGroupSystem combinedGroupSystem = new CombinedGroupSystem(); 89 | combinedGroupSystem.addSystem(keptOnReload); 90 | combinedGroupSystem.addSystem(removedOnReload); 91 | 92 | Collection survivors = combinedGroupSystem.getReloadSurvivors(); 93 | 94 | // Survivors must be collection of exactly one group, the keptOnReload 95 | // group 96 | assertEquals(1, survivors.size()); 97 | assertEquals(keptOnReload, survivors.iterator().next()); 98 | } 99 | 100 | @Test 101 | public void testSingleReturnsFalse() { 102 | CombinedGroupSystem combinedGroupSystem = new CombinedGroupSystem(); 103 | combinedGroupSystem.addSystem(new ReturnsFalseGroupSystem()); 104 | 105 | assertFalse(combinedGroupSystem.isInGroup(TestPlayer.create(), "GroupName")); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/test/java/nl/rutgerkok/blocklocker/impl/profile/NullTranslator.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.profile; 2 | 3 | import nl.rutgerkok.blocklocker.Translator; 4 | 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | import org.bukkit.command.CommandSender; 9 | 10 | /** 11 | * Simply returns the key. 12 | * 13 | */ 14 | public class NullTranslator extends Translator { 15 | 16 | @Override 17 | public String get(Translation key) { 18 | return key.toString(); 19 | } 20 | 21 | @Override 22 | public String getWithoutColor(Translation key) { 23 | return key.toString(); 24 | } 25 | 26 | @Override 27 | public void sendMessage(CommandSender player, Translation translation) { 28 | player.sendMessage(get(translation)); 29 | } 30 | 31 | @Override 32 | public List getAll(Translation key) { 33 | return Arrays.asList(key.toString()); 34 | } 35 | 36 | @Override 37 | public List getAllWithoutColor(Translation key) { 38 | return Arrays.asList(key.toString()); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/nl/rutgerkok/blocklocker/impl/profile/TestPlayerProfile.java: -------------------------------------------------------------------------------- 1 | package nl.rutgerkok.blocklocker.impl.profile; 2 | 3 | 4 | import static org.junit.jupiter.api.Assertions.assertEquals; 5 | import static org.junit.jupiter.api.Assertions.assertFalse; 6 | import static org.junit.jupiter.api.Assertions.assertTrue; 7 | 8 | import java.util.Optional; 9 | import java.util.UUID; 10 | 11 | import org.junit.jupiter.api.Test; 12 | 13 | import com.google.gson.JsonObject; 14 | 15 | import nl.rutgerkok.blocklocker.ProfileFactory; 16 | import nl.rutgerkok.blocklocker.Translator.Translation; 17 | import nl.rutgerkok.blocklocker.group.CombinedGroupSystem; 18 | import nl.rutgerkok.blocklocker.impl.JsonSecretSignEntry; 19 | import nl.rutgerkok.blocklocker.profile.PlayerProfile; 20 | import nl.rutgerkok.blocklocker.profile.Profile; 21 | 22 | public class TestPlayerProfile { 23 | 24 | private ProfileFactoryImpl getProfileFactory() { 25 | return new ProfileFactoryImpl(new CombinedGroupSystem(), new NullTranslator()); 26 | } 27 | 28 | @Test 29 | public void testIncludes() { 30 | ProfileFactoryImpl factory = getProfileFactory(); 31 | UUID bobId = UUID.randomUUID(); 32 | String everyoneTag = "[" + new NullTranslator().getWithoutColor(Translation.TAG_EVERYONE) + "]"; 33 | 34 | Profile bob = factory.fromNameAndUniqueId("Bob", Optional.of(bobId)); 35 | Profile bobRenamed = factory.fromNameAndUniqueId("Bob2", Optional.of(bobId)); 36 | Profile jane = factory.fromNameAndUniqueId("Jane", Optional.of(UUID.randomUUID())); 37 | Profile janeWithoutId = factory.fromDisplayText("jane"); 38 | Profile everyone = factory.fromDisplayText(everyoneTag); 39 | 40 | assertTrue(bob.includes(bobRenamed), "Same id"); 41 | assertTrue(bobRenamed.includes(bob), "Same id"); 42 | assertFalse(jane.includes(janeWithoutId), "Known id, not present in other"); 43 | assertTrue(janeWithoutId.includes(jane), "Unknown id, same name"); 44 | assertFalse(bob.includes(jane), "Different id and name"); 45 | assertFalse(bob.includes(janeWithoutId), "Different id and name"); 46 | 47 | // Everyone includes everyone, but is never included 48 | assertTrue(everyone.includes(bob)); 49 | assertTrue(everyone.includes(jane)); 50 | assertTrue(everyone.includes(janeWithoutId)); 51 | assertFalse(bob.includes(everyone)); 52 | assertFalse(jane.includes(everyone)); 53 | assertFalse(janeWithoutId.includes(everyone)); 54 | } 55 | 56 | @Test 57 | public void testNameAndId() { 58 | String name = "test"; 59 | UUID uuid = UUID.randomUUID(); 60 | ProfileFactory factory = getProfileFactory(); 61 | Profile profile = factory.fromNameAndUniqueId(name, Optional.of(uuid)); 62 | 63 | // Test object properties 64 | assertEquals(name, profile.getDisplayName()); 65 | assertEquals(uuid, ((PlayerProfile) profile).getUniqueId().get()); 66 | } 67 | 68 | @Test 69 | public void testNameAndIdJson() { 70 | String name = "test"; 71 | UUID uuid = UUID.randomUUID(); 72 | ProfileFactory factory = getProfileFactory(); 73 | Profile profile = factory.fromNameAndUniqueId(name, Optional.of(uuid)); 74 | JsonSecretSignEntry object = new JsonSecretSignEntry(new JsonObject()); 75 | profile.getSaveObject(object); 76 | 77 | assertEquals(name, object.getString(PlayerProfileImpl.NAME_KEY).get()); 78 | assertEquals(uuid, object.getUniqueId(PlayerProfileImpl.UUID_KEY).get()); 79 | } 80 | 81 | @Test 82 | public void testPlayerProfileRoundtrip() { 83 | String name = "test"; 84 | UUID uuid = UUID.randomUUID(); 85 | ProfileFactoryImpl factory = getProfileFactory(); 86 | Profile profile = factory.fromNameAndUniqueId(name, Optional.of(uuid)); 87 | 88 | testRoundtrip(factory, profile); 89 | } 90 | 91 | private void testRoundtrip(ProfileFactoryImpl factory, Profile profile) { 92 | JsonSecretSignEntry object = new JsonSecretSignEntry(new JsonObject()); 93 | profile.getSaveObject(object); 94 | Profile newProfile = factory.fromSavedObject(object).get(); 95 | assertEquals(profile, newProfile); 96 | } 97 | 98 | @Test 99 | public void testWithoutId() { 100 | String name = "test"; 101 | ProfileFactoryImpl factory = getProfileFactory(); 102 | Profile profile = factory.fromDisplayText(name); 103 | 104 | assertEquals(name, profile.getDisplayName()); 105 | assertFalse(((PlayerProfile) profile).getUniqueId().isPresent()); 106 | } 107 | 108 | @Test 109 | public void testWithoutIdJson() { 110 | String name = "test"; 111 | ProfileFactoryImpl factory = getProfileFactory(); 112 | Profile profile = factory.fromDisplayText(name); 113 | JsonSecretSignEntry object = new JsonSecretSignEntry(new JsonObject()); 114 | profile.getSaveObject(object); 115 | 116 | assertEquals(name, object.getString(PlayerProfileImpl.NAME_KEY).get()); 117 | assertFalse(object.getUniqueId(PlayerProfileImpl.UUID_KEY).isPresent()); 118 | } 119 | } 120 | --------------------------------------------------------------------------------