diff --git a/.gitignore b/.gitignore index 076b179b..dc07a150 100644 --- a/.gitignore +++ b/.gitignore @@ -1,34 +1,80 @@ -# Eclipse -/.classpath -/.project -/.settings -/*.jardesc +*.class -# IntelliJ -*.iml -*.ipr -*.iws -.idea/ +# Mobile Tools for Java (J2ME) +.mtj.tmp/ -# NetBeans -/nbproject +# Package Files # +*.war +*.ear -# vim -.*.sw[a-p] +# ========================= +# Ignore Eclipse Artifacts +# ========================= +.settings/ +.classpath +.project -# Maven -/build.xml -/target -/dependency-reduced-pom.xml +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* -# various other potential build files -/build -/bin -/dist -/manifest.mf +# ========================= +# Operating System Files +# ========================= + +# OSX +# ========================= -# Mac Filesystem Dust .DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows +# ========================= + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# IntelliJ +workspace.xml +tasks.xml +.idea/libraries +.idea/copyright +out/ +target/ +*.MF +*.name +.idea +*.iml -# Windows Filesystem Dust -Thumbs.db \ No newline at end of file +# VS Code +.vscode/ \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index f9837f45..4cd5c659 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,7 +4,7 @@ clone_depth: 10 environment: matrix: - appveyor_build_worker_image: Visual Studio 2022 - JAVA_HOME: C:\Program Files\Java\jdk17 + JAVA_HOME: C:\Program Files\Java\jdk21 branches: only: - master diff --git a/jitpack.yml b/jitpack.yml index d9bb5abd..a1dbc505 100644 --- a/jitpack.yml +++ b/jitpack.yml @@ -1,3 +1,4 @@ before_install: - - sdk install java 17.0.1-open - - sdk use java 17.0.1-open + - sdk install java 21.0.3-tem + - sdk use java 21.0.3-tem + - mvn wrapper:wrapper -Dmaven=3.6.3 diff --git a/pom.xml b/pom.xml index 167ad2f7..c42e08da 100644 --- a/pom.xml +++ b/pom.xml @@ -69,15 +69,15 @@ maven-compiler-plugin 3.8.1 - 16 - 16 + 21 + 21 - pl.project13.maven - git-commit-id-plugin - 4.0.3 + io.github.git-commit-id + git-commit-id-maven-plugin + 9.0.1 get-the-git-infos @@ -162,9 +162,9 @@ - io.papermc.paper - paper-api - 1.20.4-R0.1-SNAPSHOT + org.spigotmc + spigot-api + 1.21-R0.1-SNAPSHOT provided diff --git a/src/main/java/com/griefprevention/protection/ProtectionHelper.java b/src/main/java/com/griefprevention/protection/ProtectionHelper.java new file mode 100644 index 00000000..f5c8daaa --- /dev/null +++ b/src/main/java/com/griefprevention/protection/ProtectionHelper.java @@ -0,0 +1,108 @@ +package com.griefprevention.protection; + +import me.ryanhamshire.GriefPrevention.Claim; +import me.ryanhamshire.GriefPrevention.ClaimPermission; +import me.ryanhamshire.GriefPrevention.ClaimsMode; +import me.ryanhamshire.GriefPrevention.DataStore; +import me.ryanhamshire.GriefPrevention.GriefPrevention; +import me.ryanhamshire.GriefPrevention.Messages; +import me.ryanhamshire.GriefPrevention.PlayerData; +import me.ryanhamshire.GriefPrevention.events.PreventBlockBreakEvent; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Supplier; + +/** + * A utility used to simplify various protection-related checks. + */ +public final class ProtectionHelper +{ + + private ProtectionHelper() {} + + /** + * Check the {@link ClaimPermission} state for a {@link Player} at a particular {@link Location}. + * + *

This respects ignoring claims, wilderness rules, etc.

+ * + * @param player the person performing the action + * @param location the affected {@link Location} + * @param permission the required permission + * @param trigger the triggering {@link Event}, if any + * @return the denial message supplier, or {@code null} if the action is not denied + */ + public static @Nullable Supplier checkPermission( + @NotNull Player player, + @NotNull Location location, + @NotNull ClaimPermission permission, + @Nullable Event trigger) + { + World world = location.getWorld(); + if (world == null || !GriefPrevention.instance.claimsEnabledForWorld(world)) return null; + + PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId()); + + // Administrators ignoring claims always have permission. + if (playerData.ignoreClaims) return null; + + Claim claim = GriefPrevention.instance.dataStore.getClaimAt(location, false, playerData.lastClaim); + + + // If there is no claim here, use wilderness rules. + if (claim == null) + { + ClaimsMode mode = GriefPrevention.instance.config_claims_worldModes.get(world); + if (mode == ClaimsMode.Creative || mode == ClaimsMode.SurvivalRequiringClaims) + { + // Allow placing chest if it would create an automatic claim. + if (trigger instanceof BlockPlaceEvent placeEvent + && placeEvent.getBlock().getType() == Material.CHEST + && playerData.getClaims().isEmpty() + && GriefPrevention.instance.config_claims_automaticClaimsForNewPlayersRadius > -1) + return null; + + // If claims are required, provide relevant information. + return () -> + { + String reason = GriefPrevention.instance.dataStore.getMessage(Messages.NoBuildOutsideClaims); + if (player.hasPermission("griefprevention.ignoreclaims")) + reason += " " + GriefPrevention.instance.dataStore.getMessage(Messages.IgnoreClaimsAdvertisement); + reason += " " + GriefPrevention.instance.dataStore.getMessage(Messages.CreativeBasicsVideo2, DataStore.CREATIVE_VIDEO_URL); + return reason; + }; + } + + // If claims are not required, then the player has permission. + return null; + } + + // Update cached claim. + playerData.lastClaim = claim; + + // Apply claim rules. + Supplier cancel = claim.checkPermission(player, permission, trigger); + + // Apply additional specific rules. + if (cancel != null && trigger instanceof BlockBreakEvent breakEvent) + { + PreventBlockBreakEvent preventionEvent = new PreventBlockBreakEvent(breakEvent); + Bukkit.getPluginManager().callEvent(preventionEvent); + if (preventionEvent.isCancelled()) + { + cancel = null; + } + } + + return cancel; + } + +} diff --git a/src/main/java/com/griefprevention/visualization/BoundaryVisualization.java b/src/main/java/com/griefprevention/visualization/BoundaryVisualization.java index 13fb4cb1..342f1233 100644 --- a/src/main/java/com/griefprevention/visualization/BoundaryVisualization.java +++ b/src/main/java/com/griefprevention/visualization/BoundaryVisualization.java @@ -26,7 +26,7 @@ /** * A representation of a system for displaying rectangular {@link Boundary Boundaries} to {@link Player Players}. - * This is used to display claim areas, visualize affected area during nature restoration, and more. + * This is used to display claim areas, conflicting claims, and more. */ public abstract class BoundaryVisualization { diff --git a/src/main/java/com/griefprevention/visualization/VisualizationType.java b/src/main/java/com/griefprevention/visualization/VisualizationType.java index 3af06915..4c0ab317 100644 --- a/src/main/java/com/griefprevention/visualization/VisualizationType.java +++ b/src/main/java/com/griefprevention/visualization/VisualizationType.java @@ -15,8 +15,6 @@ public enum VisualizationType /** Boundaries for a new claim area. */ INITIALIZE_ZONE, /** Boundaries for a conflicting area. */ - CONFLICT_ZONE, - /** Boundaries for area in which nature has been restored. */ - NATURE_RESTORATION_ZONE + CONFLICT_ZONE } diff --git a/src/main/java/com/griefprevention/visualization/impl/FakeBlockVisualization.java b/src/main/java/com/griefprevention/visualization/impl/FakeBlockVisualization.java index fe106e42..03ded33c 100644 --- a/src/main/java/com/griefprevention/visualization/impl/FakeBlockVisualization.java +++ b/src/main/java/com/griefprevention/visualization/impl/FakeBlockVisualization.java @@ -44,7 +44,7 @@ public FakeBlockVisualization(@NotNull World world, @NotNull IntVector visualize return addBlockElement(switch (boundary.type()) { case SUBDIVISION -> Material.IRON_BLOCK.createBlockData(); - case INITIALIZE_ZONE, NATURE_RESTORATION_ZONE -> Material.DIAMOND_BLOCK.createBlockData(); + case INITIALIZE_ZONE -> Material.DIAMOND_BLOCK.createBlockData(); case CONFLICT_ZONE -> { BlockData fakeData = Material.REDSTONE_ORE.createBlockData(); ((Lightable) fakeData).setLit(true); @@ -63,7 +63,7 @@ public FakeBlockVisualization(@NotNull World world, @NotNull IntVector visualize { case ADMIN_CLAIM -> Material.PUMPKIN.createBlockData(); case SUBDIVISION -> Material.WHITE_WOOL.createBlockData(); - case INITIALIZE_ZONE, NATURE_RESTORATION_ZONE -> Material.DIAMOND_BLOCK.createBlockData(); + case INITIALIZE_ZONE -> Material.DIAMOND_BLOCK.createBlockData(); case CONFLICT_ZONE -> Material.NETHERRACK.createBlockData(); default -> Material.GOLD_BLOCK.createBlockData(); }); diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/AutoExtendClaimTask.java b/src/main/java/me/ryanhamshire/GriefPrevention/AutoExtendClaimTask.java index 5fdd083a..014e091e 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/AutoExtendClaimTask.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/AutoExtendClaimTask.java @@ -5,6 +5,8 @@ import org.bukkit.ChunkSnapshot; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Tag; import org.bukkit.World; import org.bukkit.World.Environment; import org.bukkit.block.Biome; @@ -15,6 +17,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -78,6 +81,12 @@ public static void scheduleAsync(@NotNull Claim claim) private final Map> biomePlayerMaterials = new HashMap<>(); private final int minY; private final int lowestExistingY; + // Definitions of biomes where sand covers surfaces instead of grass. + static final Set SAND_SOIL_BIOMES = Set.of( + NamespacedKey.minecraft("snowy_beach"), + NamespacedKey.minecraft("beach"), + NamespacedKey.minecraft("desert") + ); private AutoExtendClaimTask( @NotNull Claim claim, @@ -177,12 +186,232 @@ private Set getBiomePlayerBlocks(Biome biome) { return biomePlayerMaterials.computeIfAbsent(biome, newBiome -> { - Set playerBlocks = RestoreNatureProcessingTask.getPlayerBlocks(this.worldType, newBiome); + Set playerBlocks = AutoExtendClaimTask.getPlayerBlocks(this.worldType, newBiome); playerBlocks.removeAll(BlockEventHandler.TRASH_BLOCKS); return playerBlocks; }); } + static Set getPlayerBlocks(Environment environment, Biome biome) + { + Set playerBlocks = new HashSet<>(); + playerBlocks.addAll(Tag.ANVIL.getValues()); + playerBlocks.addAll(Tag.BANNERS.getValues()); + playerBlocks.addAll(Tag.BEACON_BASE_BLOCKS.getValues()); + playerBlocks.addAll(Tag.BEDS.getValues()); + playerBlocks.addAll(Tag.BUTTONS.getValues()); + playerBlocks.addAll(Tag.CAMPFIRES.getValues()); + playerBlocks.addAll(Tag.CANDLE_CAKES.getValues()); + playerBlocks.addAll(Tag.CANDLES.getValues()); + playerBlocks.addAll(Tag.WOOL_CARPETS.getValues()); + playerBlocks.addAll(Tag.CAULDRONS.getValues()); + playerBlocks.addAll(Tag.DOORS.getValues()); + playerBlocks.addAll(Tag.FENCE_GATES.getValues()); + playerBlocks.addAll(Tag.FENCES.getValues()); + playerBlocks.addAll(Tag.FIRE.getValues()); + playerBlocks.addAll(Tag.FLOWER_POTS.getValues()); + playerBlocks.addAll(Tag.IMPERMEABLE.getValues()); // Glass block variants + playerBlocks.addAll(Tag.LOGS.getValues()); + playerBlocks.addAll(Tag.PLANKS.getValues()); + playerBlocks.addAll(Tag.PRESSURE_PLATES.getValues()); + playerBlocks.addAll(Tag.RAILS.getValues()); + playerBlocks.addAll(Tag.SHULKER_BOXES.getValues()); + playerBlocks.addAll(Tag.SIGNS.getValues()); + playerBlocks.addAll(Tag.SLABS.getValues()); + playerBlocks.addAll(Tag.STAIRS.getValues()); + playerBlocks.addAll(Tag.STONE_BRICKS.getValues()); + playerBlocks.addAll(Tag.TRAPDOORS.getValues()); + playerBlocks.addAll(Tag.WALLS.getValues()); + playerBlocks.addAll(Tag.WOOL.getValues()); + playerBlocks.add(Material.BOOKSHELF); + playerBlocks.add(Material.BREWING_STAND); + playerBlocks.add(Material.BRICK); + playerBlocks.add(Material.COBBLESTONE); + playerBlocks.add(Material.LAPIS_BLOCK); + playerBlocks.add(Material.DISPENSER); + playerBlocks.add(Material.NOTE_BLOCK); + playerBlocks.add(Material.STICKY_PISTON); + playerBlocks.add(Material.PISTON); + playerBlocks.add(Material.PISTON_HEAD); + playerBlocks.add(Material.MOVING_PISTON); + playerBlocks.add(Material.WHEAT); + playerBlocks.add(Material.TNT); + playerBlocks.add(Material.MOSSY_COBBLESTONE); + playerBlocks.add(Material.TORCH); + playerBlocks.add(Material.CHEST); + playerBlocks.add(Material.REDSTONE_WIRE); + playerBlocks.add(Material.CRAFTING_TABLE); + playerBlocks.add(Material.FURNACE); + playerBlocks.add(Material.LADDER); + playerBlocks.add(Material.SCAFFOLDING); + playerBlocks.add(Material.LEVER); + playerBlocks.add(Material.REDSTONE_TORCH); + playerBlocks.add(Material.SNOW_BLOCK); + playerBlocks.add(Material.JUKEBOX); + playerBlocks.add(Material.NETHER_PORTAL); + playerBlocks.add(Material.JACK_O_LANTERN); + playerBlocks.add(Material.CAKE); + playerBlocks.add(Material.REPEATER); + playerBlocks.add(Material.MUSHROOM_STEM); + playerBlocks.add(Material.RED_MUSHROOM_BLOCK); + playerBlocks.add(Material.BROWN_MUSHROOM_BLOCK); + playerBlocks.add(Material.IRON_BARS); + playerBlocks.add(Material.GLASS_PANE); + playerBlocks.add(Material.MELON_STEM); + playerBlocks.add(Material.ENCHANTING_TABLE); + playerBlocks.add(Material.COBWEB); + playerBlocks.add(Material.GRAVEL); + playerBlocks.add(Material.SANDSTONE); + playerBlocks.add(Material.ENDER_CHEST); + playerBlocks.add(Material.COMMAND_BLOCK); + playerBlocks.add(Material.REPEATING_COMMAND_BLOCK); + playerBlocks.add(Material.CHAIN_COMMAND_BLOCK); + playerBlocks.add(Material.BEACON); + playerBlocks.add(Material.CARROT); + playerBlocks.add(Material.POTATO); + playerBlocks.add(Material.SKELETON_SKULL); + playerBlocks.add(Material.WITHER_SKELETON_SKULL); + playerBlocks.add(Material.CREEPER_HEAD); + playerBlocks.add(Material.ZOMBIE_HEAD); + playerBlocks.add(Material.PLAYER_HEAD); + playerBlocks.add(Material.DRAGON_HEAD); + playerBlocks.add(Material.SPONGE); + playerBlocks.add(Material.WHITE_STAINED_GLASS_PANE); + playerBlocks.add(Material.ORANGE_STAINED_GLASS_PANE); + playerBlocks.add(Material.MAGENTA_STAINED_GLASS_PANE); + playerBlocks.add(Material.LIGHT_BLUE_STAINED_GLASS_PANE); + playerBlocks.add(Material.YELLOW_STAINED_GLASS_PANE); + playerBlocks.add(Material.LIME_STAINED_GLASS_PANE); + playerBlocks.add(Material.PINK_STAINED_GLASS_PANE); + playerBlocks.add(Material.GRAY_STAINED_GLASS_PANE); + playerBlocks.add(Material.LIGHT_GRAY_STAINED_GLASS_PANE); + playerBlocks.add(Material.CYAN_STAINED_GLASS_PANE); + playerBlocks.add(Material.PURPLE_STAINED_GLASS_PANE); + playerBlocks.add(Material.BLUE_STAINED_GLASS_PANE); + playerBlocks.add(Material.BROWN_STAINED_GLASS_PANE); + playerBlocks.add(Material.GREEN_STAINED_GLASS_PANE); + playerBlocks.add(Material.RED_STAINED_GLASS_PANE); + playerBlocks.add(Material.BLACK_STAINED_GLASS_PANE); + playerBlocks.add(Material.TRAPPED_CHEST); + playerBlocks.add(Material.COMPARATOR); + playerBlocks.add(Material.DAYLIGHT_DETECTOR); + playerBlocks.add(Material.REDSTONE_BLOCK); + playerBlocks.add(Material.HOPPER); + playerBlocks.add(Material.QUARTZ_BLOCK); + playerBlocks.add(Material.DROPPER); + playerBlocks.add(Material.SLIME_BLOCK); + playerBlocks.add(Material.PRISMARINE); + playerBlocks.add(Material.HAY_BLOCK); + playerBlocks.add(Material.SEA_LANTERN); + playerBlocks.add(Material.COAL_BLOCK); + playerBlocks.add(Material.REDSTONE_LAMP); + playerBlocks.add(Material.RED_NETHER_BRICKS); + playerBlocks.add(Material.POLISHED_ANDESITE); + playerBlocks.add(Material.POLISHED_DIORITE); + playerBlocks.add(Material.POLISHED_GRANITE); + playerBlocks.add(Material.POLISHED_BASALT); + playerBlocks.add(Material.POLISHED_DEEPSLATE); + playerBlocks.add(Material.DEEPSLATE_BRICKS); + playerBlocks.add(Material.CRACKED_DEEPSLATE_BRICKS); + playerBlocks.add(Material.DEEPSLATE_TILES); + playerBlocks.add(Material.CRACKED_DEEPSLATE_TILES); + playerBlocks.add(Material.CHISELED_DEEPSLATE); + playerBlocks.add(Material.RAW_COPPER_BLOCK); + playerBlocks.add(Material.RAW_IRON_BLOCK); + playerBlocks.add(Material.RAW_GOLD_BLOCK); + playerBlocks.add(Material.LIGHTNING_ROD); + playerBlocks.add(Material.DECORATED_POT); + + //these are unnatural in the nether and end + if (environment != Environment.NORMAL && environment != Environment.CUSTOM) + { + playerBlocks.addAll(Tag.BASE_STONE_OVERWORLD.getValues()); + playerBlocks.addAll(Tag.DIRT.getValues()); + playerBlocks.addAll(Tag.SAND.getValues()); + } + + //these are unnatural in the standard world, but not in the nether + if (environment != Environment.NETHER) + { + playerBlocks.addAll(Tag.NYLIUM.getValues()); + playerBlocks.addAll(Tag.WART_BLOCKS.getValues()); + playerBlocks.addAll(Tag.BASE_STONE_NETHER.getValues()); + playerBlocks.add(Material.POLISHED_BLACKSTONE); + playerBlocks.add(Material.CHISELED_POLISHED_BLACKSTONE); + playerBlocks.add(Material.CRACKED_POLISHED_BLACKSTONE_BRICKS); + playerBlocks.add(Material.GILDED_BLACKSTONE); + playerBlocks.add(Material.BONE_BLOCK); + playerBlocks.add(Material.SOUL_SAND); + playerBlocks.add(Material.SOUL_SOIL); + playerBlocks.add(Material.GLOWSTONE); + playerBlocks.add(Material.NETHER_BRICK); + playerBlocks.add(Material.MAGMA_BLOCK); + playerBlocks.add(Material.ANCIENT_DEBRIS); + playerBlocks.add(Material.CHAIN); + playerBlocks.add(Material.SHROOMLIGHT); + playerBlocks.add(Material.NETHER_GOLD_ORE); + playerBlocks.add(Material.NETHER_SPROUTS); + playerBlocks.add(Material.CRIMSON_FUNGUS); + playerBlocks.add(Material.CRIMSON_ROOTS); + playerBlocks.add(Material.NETHER_WART_BLOCK); + playerBlocks.add(Material.WEEPING_VINES); + playerBlocks.add(Material.WEEPING_VINES_PLANT); + playerBlocks.add(Material.WARPED_FUNGUS); + playerBlocks.add(Material.WARPED_ROOTS); + playerBlocks.add(Material.WARPED_WART_BLOCK); + playerBlocks.add(Material.TWISTING_VINES); + playerBlocks.add(Material.TWISTING_VINES_PLANT); + } + //blocks from tags that are natural in the nether + else + { + playerBlocks.remove(Material.CRIMSON_STEM); + playerBlocks.remove(Material.CRIMSON_HYPHAE); + playerBlocks.remove(Material.NETHER_BRICK_FENCE); + playerBlocks.remove(Material.NETHER_BRICK_STAIRS); + playerBlocks.remove(Material.SOUL_FIRE); + playerBlocks.remove(Material.WARPED_STEM); + playerBlocks.remove(Material.WARPED_HYPHAE); + } + + //these are unnatural in the standard and nether worlds, but not in the end + if (environment != Environment.THE_END) + { + playerBlocks.add(Material.CHORUS_PLANT); + playerBlocks.add(Material.CHORUS_FLOWER); + playerBlocks.add(Material.END_ROD); + playerBlocks.add(Material.END_STONE); + playerBlocks.add(Material.END_STONE_BRICKS); + playerBlocks.add(Material.OBSIDIAN); + playerBlocks.add(Material.PURPUR_BLOCK); + playerBlocks.add(Material.PURPUR_PILLAR); + } + //blocks from tags that are natural in the end + else + { + playerBlocks.remove(Material.PURPUR_SLAB); + playerBlocks.remove(Material.PURPUR_STAIRS); + } + + //these are unnatural in sandy biomes, but not elsewhere + if (SAND_SOIL_BIOMES.contains(biome.getKey()) || environment != Environment.NORMAL) + { + playerBlocks.addAll(Tag.LEAVES.getValues()); + } + //blocks from tags that are natural in non-sandy normal biomes + else + { + playerBlocks.remove(Material.OAK_LOG); + playerBlocks.remove(Material.SPRUCE_LOG); + playerBlocks.remove(Material.BIRCH_LOG); + playerBlocks.remove(Material.JUNGLE_LOG); + playerBlocks.remove(Material.ACACIA_LOG); + playerBlocks.remove(Material.DARK_OAK_LOG); + } + + return playerBlocks; + } + //runs in the main execution thread, where it can safely change claims and save those changes private record ExecuteExtendClaimTask(Claim claim, int newY) implements Runnable { diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/BlockEventHandler.java b/src/main/java/me/ryanhamshire/GriefPrevention/BlockEventHandler.java index 35fa5da1..2c341dd5 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/BlockEventHandler.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/BlockEventHandler.java @@ -21,6 +21,7 @@ import com.griefprevention.visualization.BoundaryVisualization; import com.griefprevention.visualization.VisualizationType; import me.ryanhamshire.GriefPrevention.util.BoundingBox; +import com.griefprevention.protection.ProtectionHelper; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.GameMode; @@ -126,10 +127,10 @@ public void onBlockBreak(BlockBreakEvent breakEvent) Block block = breakEvent.getBlock(); //make sure the player is allowed to break at the location - String noBuildReason = GriefPrevention.instance.allowBreak(player, block, block.getLocation(), breakEvent); + Supplier noBuildReason = ProtectionHelper.checkPermission(player, block.getLocation(), ClaimPermission.Build, breakEvent); if (noBuildReason != null) { - GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason); + GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason.get()); breakEvent.setCancelled(true); return; } @@ -144,10 +145,10 @@ public void onSignChanged(SignChangeEvent event) if (player == null || sign == null) return; - String noBuildReason = GriefPrevention.instance.allowBuild(player, sign.getLocation(), sign.getType()); + Supplier noBuildReason = ProtectionHelper.checkPermission(player, sign.getLocation(), ClaimPermission.Build, event); if (noBuildReason != null) { - GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason); + GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason.get()); event.setCancelled(true); return; } @@ -213,10 +214,10 @@ public void onBlocksPlace(BlockMultiPlaceEvent placeEvent) //make sure the player is allowed to build at the location for (BlockState block : placeEvent.getReplacedBlockStates()) { - String noBuildReason = GriefPrevention.instance.allowBuild(player, block.getLocation(), block.getType()); + Supplier noBuildReason = ProtectionHelper.checkPermission(player, block.getLocation(), ClaimPermission.Build, placeEvent); if (noBuildReason != null) { - GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason); + GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason.get()); placeEvent.setCancelled(true); return; } @@ -271,7 +272,7 @@ public void onBlockPlace(BlockPlaceEvent placeEvent) if (!GriefPrevention.instance.claimsEnabledForWorld(placeEvent.getBlock().getWorld())) return; //make sure the player is allowed to build at the location - String noBuildReason = GriefPrevention.instance.allowBuild(player, block.getLocation(), block.getType()); + Supplier noBuildReason = ProtectionHelper.checkPermission(player, block.getLocation(), ClaimPermission.Build, placeEvent); if (noBuildReason != null) { // Allow players with container trust to place books in lecterns @@ -291,7 +292,7 @@ public void onBlockPlace(BlockPlaceEvent placeEvent) return; } } - GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason); + GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason.get()); placeEvent.setCancelled(true); return; } diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/BlockSnapshot.java b/src/main/java/me/ryanhamshire/GriefPrevention/BlockSnapshot.java deleted file mode 100644 index 8f198936..00000000 --- a/src/main/java/me/ryanhamshire/GriefPrevention/BlockSnapshot.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - GriefPrevention Server Plugin for Minecraft - Copyright (C) 2012 Ryan Hamshire - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - */ - -package me.ryanhamshire.GriefPrevention; - -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.data.BlockData; - -//basically, just a few data points from a block conveniently encapsulated in a class -//this is used only by the RestoreNature code -public class BlockSnapshot -{ - public Location location; - public Material typeId; - public BlockData data; - - public BlockSnapshot(Location location, Material typeId, BlockData data) - { - this.location = location; - this.typeId = typeId; - this.data = data; - } -} diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/Claim.java b/src/main/java/me/ryanhamshire/GriefPrevention/Claim.java index 5cdc074c..a8537cd6 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/Claim.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/Claim.java @@ -123,86 +123,6 @@ public Long getID() this.modifiedDate = Calendar.getInstance().getTime(); } - //removes any lava above sea level in a claim - //exclusionClaim is another claim indicating an sub-area to be excluded from this operation - //it may be null - public void removeSurfaceFluids(Claim exclusionClaim) - { - //don't do this for administrative claims - if (this.isAdminClaim()) return; - - //don't do it for very large claims - if (this.getArea() > 10000) return; - - //only in creative mode worlds - if (!GriefPrevention.instance.creativeRulesApply(this.lesserBoundaryCorner)) return; - - Location lesser = this.getLesserBoundaryCorner(); - Location greater = this.getGreaterBoundaryCorner(); - - if (lesser.getWorld().getEnvironment() == Environment.NETHER) return; //don't clean up lava in the nether - - int seaLevel = 0; //clean up all fluids in the end - - //respect sea level in normal worlds - if (lesser.getWorld().getEnvironment() == Environment.NORMAL) - seaLevel = GriefPrevention.instance.getSeaLevel(lesser.getWorld()); - - for (int x = lesser.getBlockX(); x <= greater.getBlockX(); x++) - { - for (int z = lesser.getBlockZ(); z <= greater.getBlockZ(); z++) - { - for (int y = seaLevel - 1; y <= lesser.getWorld().getMaxHeight(); y++) - { - //dodge the exclusion claim - Block block = lesser.getWorld().getBlockAt(x, y, z); - if (exclusionClaim != null && exclusionClaim.contains(block.getLocation(), true, false)) continue; - - if (block.getType() == Material.LAVA || block.getType() == Material.WATER) - { - block.setType(Material.AIR); - } - } - } - } - } - - //determines whether or not a claim has surface lava - //used to warn players when they abandon their claims about automatic fluid cleanup - boolean hasSurfaceFluids() - { - Location lesser = this.getLesserBoundaryCorner(); - Location greater = this.getGreaterBoundaryCorner(); - - //don't bother for very large claims, too expensive - if (this.getArea() > 10000) return false; - - int seaLevel = 0; //clean up all fluids in the end - - //respect sea level in normal worlds - if (lesser.getWorld().getEnvironment() == Environment.NORMAL) - seaLevel = GriefPrevention.instance.getSeaLevel(lesser.getWorld()); - - for (int x = lesser.getBlockX(); x <= greater.getBlockX(); x++) - { - for (int z = lesser.getBlockZ(); z <= greater.getBlockZ(); z++) - { - for (int y = seaLevel - 1; y <= lesser.getWorld().getMaxHeight(); y++) - { - //dodge the exclusion claim - Block block = lesser.getWorld().getBlockAt(x, y, z); - - if (block.getType() == Material.WATER || block.getType() == Material.LAVA) - { - return true; - } - } - } - } - - return false; - } - //main constructor. note that only creating a claim instance does nothing - a claim must be added to the data store to be effective Claim(Location lesserBoundaryCorner, Location greaterBoundaryCorner, UUID ownerID, List builderIDs, List containerIDs, List accessorIDs, List managerIDs, boolean inheritNothing, Long id) { @@ -873,63 +793,6 @@ boolean greaterThan(Claim otherClaim) } - long getPlayerInvestmentScore() - { - //decide which blocks will be considered player placed - Location lesserBoundaryCorner = this.getLesserBoundaryCorner(); - Set playerBlocks = RestoreNatureProcessingTask.getPlayerBlocks(lesserBoundaryCorner.getWorld().getEnvironment(), lesserBoundaryCorner.getBlock().getBiome()); - - //scan the claim for player placed blocks - double score = 0; - - boolean creativeMode = GriefPrevention.instance.creativeRulesApply(lesserBoundaryCorner); - - for (int x = this.lesserBoundaryCorner.getBlockX(); x <= this.greaterBoundaryCorner.getBlockX(); x++) - { - for (int z = this.lesserBoundaryCorner.getBlockZ(); z <= this.greaterBoundaryCorner.getBlockZ(); z++) - { - int y = this.lesserBoundaryCorner.getBlockY(); - for (; y < GriefPrevention.instance.getSeaLevel(this.lesserBoundaryCorner.getWorld()) - 5; y++) - { - Block block = this.lesserBoundaryCorner.getWorld().getBlockAt(x, y, z); - if (playerBlocks.contains(block.getType())) - { - if (block.getType() == Material.CHEST && !creativeMode) - { - score += 10; - } - else - { - score += .5; - } - } - } - - for (; y < this.lesserBoundaryCorner.getWorld().getMaxHeight(); y++) - { - Block block = this.lesserBoundaryCorner.getWorld().getBlockAt(x, y, z); - if (playerBlocks.contains(block.getType())) - { - if (block.getType() == Material.CHEST && !creativeMode) - { - score += 10; - } - else if (creativeMode && (block.getType() == Material.LAVA)) - { - score -= 10; - } - else - { - score += 1; - } - } - } - } - } - - return (long) score; - } - public ArrayList getChunks() { ArrayList chunks = new ArrayList<>(); diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/CleanupUnusedClaimTask.java b/src/main/java/me/ryanhamshire/GriefPrevention/CleanupUnusedClaimTask.java index ce3f1c6f..81189a83 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/CleanupUnusedClaimTask.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/CleanupUnusedClaimTask.java @@ -61,15 +61,8 @@ public void run() { if (expireEventCanceled()) return; - claim.removeSurfaceFluids(null); GriefPrevention.instance.dataStore.deleteClaim(claim, true, true); - //if configured to do so, restore the land to natural - if (GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()) || GriefPrevention.instance.config_claims_survivalAutoNatureRestoration) - { - GriefPrevention.instance.restoreClaim(claim, 0); - } - GriefPrevention.AddLogEntry(" " + claim.getOwnerName() + "'s new player claim expired.", CustomLogEntryTypes.AdminActivity); } } @@ -92,43 +85,6 @@ else if (GriefPrevention.instance.config_claims_expirationDays > 0) GriefPrevention.AddLogEntry(" All of " + claim.getOwnerName() + "'s claims have expired.", CustomLogEntryTypes.AdminActivity); GriefPrevention.AddLogEntry("earliestPermissibleLastLogin#getTime: " + earliestPermissibleLastLogin.getTime(), CustomLogEntryTypes.Debug, true); GriefPrevention.AddLogEntry("ownerInfo#getLastPlayed: " + ownerInfo.getLastPlayed(), CustomLogEntryTypes.Debug, true); - - for (Claim claim : claims) - { - //if configured to do so, restore the land to natural - if (GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()) || GriefPrevention.instance.config_claims_survivalAutoNatureRestoration) - { - GriefPrevention.instance.restoreClaim(claim, 0); - } - } - } - } - else if (GriefPrevention.instance.config_claims_unusedClaimExpirationDays > 0 && GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner())) - { - //avoid scanning large claims and administrative claims - if (claim.isAdminClaim() || claim.getWidth() > 25 || claim.getHeight() > 25) return; - - //otherwise scan the claim content - int minInvestment = 400; - - long investmentScore = claim.getPlayerInvestmentScore(); - - if (investmentScore < minInvestment) - { - //if the owner has been gone at least a week, and if he has ONLY the new player claim, it will be removed - Calendar sevenDaysAgo = Calendar.getInstance(); - sevenDaysAgo.add(Calendar.DATE, -GriefPrevention.instance.config_claims_unusedClaimExpirationDays); - boolean claimExpired = sevenDaysAgo.getTime().after(new Date(ownerInfo.getLastPlayed())); - if (claimExpired) - { - if (expireEventCanceled()) - return; - GriefPrevention.instance.dataStore.deleteClaim(claim, true, true); - GriefPrevention.AddLogEntry("Removed " + claim.getOwnerName() + "'s unused claim @ " + GriefPrevention.getfriendlyLocationString(claim.getLesserBoundaryCorner()), CustomLogEntryTypes.AdminActivity); - - //restore the claim area to natural state - GriefPrevention.instance.restoreClaim(claim, 0); - } } } } diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/CustomizableMessage.java b/src/main/java/me/ryanhamshire/GriefPrevention/CustomizableMessage.java deleted file mode 100644 index b0cb406d..00000000 --- a/src/main/java/me/ryanhamshire/GriefPrevention/CustomizableMessage.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - GriefPrevention Server Plugin for Minecraft - Copyright (C) 2012 Ryan Hamshire - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - */ - -package me.ryanhamshire.GriefPrevention; - -public class CustomizableMessage -{ - public Messages id; - public String text; - public String notes; - - public CustomizableMessage(Messages id, String text, String notes) - { - this.id = id; - this.text = text; - this.notes = notes; - } -} \ No newline at end of file diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/DataStore.java b/src/main/java/me/ryanhamshire/GriefPrevention/DataStore.java index 89b5e49d..b8701431 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/DataStore.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/DataStore.java @@ -48,7 +48,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -1126,15 +1125,7 @@ synchronized public void deleteClaimsForPlayer(UUID playerID, boolean releasePet //delete them one by one for (Claim claim : claimsToDelete) { - claim.removeSurfaceFluids(null); - this.deleteClaim(claim); - - //if in a creative mode world, delete the claim - if (GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner())) - { - GriefPrevention.instance.restoreClaim(claim, 0); - } } } @@ -1236,21 +1227,6 @@ void resizeClaimWithChecks(Player player, PlayerData playerData, int newx1, int //return here if event is cancelled if (event.isCancelled()) return; - //special rule for making a top-level claim smaller. to check this, verifying the old claim's corners are inside the new claim's boundaries. - //rule: in any mode, shrinking a claim removes any surface fluids - boolean smaller = false; - if (oldClaim.parent == null) - { - //if the new claim is smaller - if (!newClaim.contains(oldClaim.getLesserBoundaryCorner(), true, false) || !newClaim.contains(oldClaim.getGreaterBoundaryCorner(), true, false)) - { - smaller = true; - - //remove surface fluids about to be unclaimed - oldClaim.removeSurfaceFluids(newClaim); - } - } - //ask the datastore to try and resize the claim, this checks for conflicts with other claims CreateClaimResult result = GriefPrevention.instance.dataStore.resizeClaim( playerData.claimResizing, @@ -1306,14 +1282,6 @@ void resizeClaimWithChecks(Player player, PlayerData playerData, int newx1, int GriefPrevention.sendMessage(player, TextMode.Instr, Messages.SubdivisionVideo2, 201L, DataStore.SUBDIVISION_VIDEO_URL); } - //if in a creative mode world and shrinking an existing claim, restore any unclaimed area - if (smaller && GriefPrevention.instance.creativeRulesApply(oldClaim.getLesserBoundaryCorner())) - { - GriefPrevention.sendMessage(player, TextMode.Warn, Messages.UnclaimCleanupWarning); - GriefPrevention.instance.restoreClaim(oldClaim, 20L * 60 * 2); //2 minutes - GriefPrevention.AddLogEntry(player.getName() + " shrank a claim @ " + GriefPrevention.getfriendlyLocationString(playerData.claimResizing.getLesserBoundaryCorner())); - } - //clean up playerData.claimResizing = null; playerData.lastShovelLocation = null; @@ -1355,268 +1323,60 @@ else if (player.hasPermission("griefprevention.adjustclaimblocks")) protected void loadMessages() { Messages[] messageIDs = Messages.values(); - this.messages = new String[Messages.values().length]; - - HashMap defaults = new HashMap<>(); - - //initialize defaults - this.addDefault(defaults, Messages.RespectingClaims, "Now respecting claims.", null); - this.addDefault(defaults, Messages.IgnoringClaims, "Now ignoring claims.", null); - this.addDefault(defaults, Messages.NoCreativeUnClaim, "You can't unclaim this land. You can only make this claim larger or create additional claims.", null); - this.addDefault(defaults, Messages.SuccessfulAbandon, "Claims abandoned. You now have {0} available claim blocks.", "0: remaining blocks"); - this.addDefault(defaults, Messages.RestoreNatureActivate, "Ready to restore some nature! Right click to restore nature, and use /basicclaims to stop.", null); - this.addDefault(defaults, Messages.RestoreNatureAggressiveActivate, "Aggressive mode activated. Do NOT use this underneath anything you want to keep! Right click to aggressively restore nature, and use /basicclaims to stop.", null); - this.addDefault(defaults, Messages.FillModeActive, "Fill mode activated with radius {0}. Right click an area to fill.", "0: fill radius"); - this.addDefault(defaults, Messages.TransferClaimPermission, "That command requires the administrative claims permission.", null); - this.addDefault(defaults, Messages.TransferClaimMissing, "There's no claim here. Stand in the administrative claim you want to transfer.", null); - this.addDefault(defaults, Messages.TransferClaimAdminOnly, "Only administrative claims may be transferred to a player.", null); - this.addDefault(defaults, Messages.PlayerNotFound2, "No player by that name has logged in recently.", null); - this.addDefault(defaults, Messages.TransferTopLevel, "Only top level claims (not subdivisions) may be transferred. Stand outside of the subdivision and try again.", null); - this.addDefault(defaults, Messages.TransferSuccess, "Claim transferred.", null); - this.addDefault(defaults, Messages.TrustListNoClaim, "Stand inside the claim you're curious about.", null); - this.addDefault(defaults, Messages.ClearPermsOwnerOnly, "Only the claim owner can clear all permissions.", null); - this.addDefault(defaults, Messages.UntrustIndividualAllClaims, "Revoked {0}'s access to ALL your claims. To set permissions for a single claim, stand inside it.", "0: untrusted player"); - this.addDefault(defaults, Messages.UntrustEveryoneAllClaims, "Cleared permissions in ALL your claims. To set permissions for a single claim, stand inside it.", null); - this.addDefault(defaults, Messages.NoPermissionTrust, "You don't have {0}'s permission to manage permissions here.", "0: claim owner's name"); - this.addDefault(defaults, Messages.ClearPermissionsOneClaim, "Cleared permissions in this claim. To set permission for ALL your claims, stand outside them.", null); - this.addDefault(defaults, Messages.UntrustIndividualSingleClaim, "Revoked {0}'s access to this claim. To set permissions for a ALL your claims, stand outside them.", "0: untrusted player"); - this.addDefault(defaults, Messages.AdminClaimsMode, "Administrative claims mode active. Any claims created will be free and editable by other administrators.", null); - this.addDefault(defaults, Messages.BasicClaimsMode, "Returned to basic claim creation mode.", null); - this.addDefault(defaults, Messages.SubdivisionMode, "Subdivision mode. Use your shovel to create subdivisions in your existing claims. Use /basicclaims to exit.", null); - this.addDefault(defaults, Messages.SubdivisionVideo2, "Click for Subdivision Help: {0}", "0:video URL"); - this.addDefault(defaults, Messages.DeleteClaimMissing, "There's no claim here.", null); - this.addDefault(defaults, Messages.DeletionSubdivisionWarning, "This claim includes subdivisions. If you're sure you want to delete it, use /deleteclaim again.", null); - this.addDefault(defaults, Messages.DeleteSuccess, "Claim deleted.", null); - this.addDefault(defaults, Messages.CantDeleteAdminClaim, "You don't have permission to delete administrative claims.", null); - this.addDefault(defaults, Messages.DeleteAllSuccess, "Deleted all of {0}'s claims.", "0: owner's name"); - this.addDefault(defaults, Messages.NoDeletePermission, "You don't have permission to delete claims.", null); - this.addDefault(defaults, Messages.AllAdminDeleted, "Deleted all administrative claims.", null); - this.addDefault(defaults, Messages.AdjustBlocksSuccess, "Adjusted {0}'s bonus claim blocks by {1}. New total bonus blocks: {2}.", "0: player; 1: adjustment; 2: new total"); - this.addDefault(defaults, Messages.AdjustBlocksAllSuccess, "Adjusted all online players' bonus claim blocks by {0}.", "0: adjustment amount"); - this.addDefault(defaults, Messages.NotTrappedHere, "You can build here. Save yourself.", null); - this.addDefault(defaults, Messages.RescuePending, "If you stay put for 10 seconds, you'll be teleported out. Please wait.", null); - this.addDefault(defaults, Messages.AbandonClaimMissing, "Stand in the claim you want to delete, or consider /abandonallclaims.", null); - this.addDefault(defaults, Messages.NotYourClaim, "This isn't your claim.", null); - this.addDefault(defaults, Messages.DeleteTopLevelClaim, "To delete a subdivision, stand inside it. Otherwise, use /abandontoplevelclaim to delete this claim and all subdivisions.", null); - this.addDefault(defaults, Messages.AbandonSuccess, "Claim abandoned. You now have {0} available claim blocks.", "0: remaining claim blocks"); - this.addDefault(defaults, Messages.ConfirmAbandonAllClaims, "Are you sure you want to abandon ALL of your claims? Please confirm with /abandonallclaims confirm", null); - this.addDefault(defaults, Messages.CantGrantThatPermission, "You can't grant a permission you don't have yourself.", null); - this.addDefault(defaults, Messages.GrantPermissionNoClaim, "Stand inside the claim where you want to grant permission.", null); - this.addDefault(defaults, Messages.GrantPermissionConfirmation, "Granted {0} permission to {1} {2}.", "0: target player; 1: permission description; 2: scope (changed claims)"); - this.addDefault(defaults, Messages.ManageUniversalPermissionsInstruction, "To manage permissions for ALL your claims, stand outside them.", null); - this.addDefault(defaults, Messages.ManageOneClaimPermissionsInstruction, "To manage permissions for a specific claim, stand inside it.", null); - this.addDefault(defaults, Messages.CollectivePublic, "the public", "as in 'granted the public permission to...'"); - this.addDefault(defaults, Messages.BuildPermission, "build", null); - this.addDefault(defaults, Messages.ContainersPermission, "access containers and animals", null); - this.addDefault(defaults, Messages.AccessPermission, "use buttons and levers", null); - this.addDefault(defaults, Messages.PermissionsPermission, "manage permissions", null); - this.addDefault(defaults, Messages.LocationCurrentClaim, "in this claim", null); - this.addDefault(defaults, Messages.LocationAllClaims, "in all your claims", null); - this.addDefault(defaults, Messages.PvPImmunityStart, "You're protected from attack by other players as long as your inventory is empty.", null); - this.addDefault(defaults, Messages.DonateItemsInstruction, "To give away the item(s) in your hand, left-click the chest again.", null); - this.addDefault(defaults, Messages.ChestFull, "This chest is full.", null); - this.addDefault(defaults, Messages.DonationSuccess, "Item(s) transferred to chest!", null); - this.addDefault(defaults, Messages.PlayerTooCloseForFire2, "You can't start a fire this close to another player.", null); - this.addDefault(defaults, Messages.TooDeepToClaim, "This chest can't be protected because it's too deep underground. Consider moving it.", null); - this.addDefault(defaults, Messages.ChestClaimConfirmation, "This chest is protected.", null); - this.addDefault(defaults, Messages.AutomaticClaimNotification, "This chest and nearby blocks are protected from breakage and theft.", null); - this.addDefault(defaults, Messages.AutomaticClaimOtherClaimTooClose, "Cannot create a claim for your chest, there is another claim too close!", null); - this.addDefault(defaults, Messages.UnprotectedChestWarning, "This chest is NOT protected. Consider using a golden shovel to expand an existing claim or to create a new one.", null); - this.addDefault(defaults, Messages.ThatPlayerPvPImmune, "You can't injure defenseless players.", null); - this.addDefault(defaults, Messages.CantFightWhileImmune, "You can't fight someone while you're protected from PvP.", null); - this.addDefault(defaults, Messages.NoDamageClaimedEntity, "That belongs to {0}.", "0: owner name"); - this.addDefault(defaults, Messages.ShovelBasicClaimMode, "Shovel returned to basic claims mode.", null); - this.addDefault(defaults, Messages.RemainingBlocks, "You may claim up to {0} more blocks.", "0: remaining blocks"); - this.addDefault(defaults, Messages.CreativeBasicsVideo2, "Click for Land Claim Help: {0}", "{0}: video URL"); - this.addDefault(defaults, Messages.SurvivalBasicsVideo2, "Click for Land Claim Help: {0}", "{0}: video URL"); - this.addDefault(defaults, Messages.TrappedChatKeyword, "trapped;stuck", "When mentioned in chat, players get information about the /trapped command (multiple words can be separated with semi-colons)"); - this.addDefault(defaults, Messages.TrappedInstructions, "Are you trapped in someone's land claim? Try the /trapped command.", null); - this.addDefault(defaults, Messages.PvPNoDrop, "You can't drop items while in PvP combat.", null); - this.addDefault(defaults, Messages.PvPNoContainers, "You can't access containers during PvP combat.", null); - this.addDefault(defaults, Messages.PvPImmunityEnd, "Now you can fight with other players.", null); - this.addDefault(defaults, Messages.NoBedPermission, "{0} hasn't given you permission to sleep here.", "0: claim owner"); - this.addDefault(defaults, Messages.NoWildernessBuckets, "You may only dump buckets inside your claim(s) or underground.", null); - this.addDefault(defaults, Messages.NoLavaNearOtherPlayer, "You can't place lava this close to {0}.", "0: nearby player"); - this.addDefault(defaults, Messages.TooFarAway, "That's too far away.", null); - this.addDefault(defaults, Messages.BlockNotClaimed, "No one has claimed this block.", null); - this.addDefault(defaults, Messages.BlockClaimed, "That block has been claimed by {0}.", "0: claim owner"); - this.addDefault(defaults, Messages.RestoreNaturePlayerInChunk, "Unable to restore. {0} is in that chunk.", "0: nearby player"); - this.addDefault(defaults, Messages.NoCreateClaimPermission, "You don't have permission to claim land.", null); - this.addDefault(defaults, Messages.ResizeClaimTooNarrow, "This new size would be too small. Claims must be at least {0} blocks wide.", "0: minimum claim width"); - this.addDefault(defaults, Messages.ResizeNeedMoreBlocks, "You don't have enough blocks for this size. You need {0} more.", "0: how many needed"); - this.addDefault(defaults, Messages.ClaimResizeSuccess, "Claim resized. {0} available claim blocks remaining.", "0: remaining blocks"); - this.addDefault(defaults, Messages.ResizeFailOverlap, "Can't resize here because it would overlap another nearby claim.", null); - this.addDefault(defaults, Messages.ResizeStart, "Resizing claim. Use your shovel again at the new location for this corner.", null); - this.addDefault(defaults, Messages.ResizeFailOverlapSubdivision, "You can't create a subdivision here because it would overlap another subdivision. Consider /abandonclaim to delete it, or use your shovel at a corner to resize it.", null); - this.addDefault(defaults, Messages.SubdivisionStart, "Subdivision corner set! Use your shovel at the location for the opposite corner of this new subdivision.", null); - this.addDefault(defaults, Messages.CreateSubdivisionOverlap, "Your selected area overlaps another subdivision.", null); - this.addDefault(defaults, Messages.SubdivisionSuccess, "Subdivision created! Use /trust to share it with friends.", null); - this.addDefault(defaults, Messages.CreateClaimFailOverlap, "You can't create a claim here because it would overlap your other claim. Use /abandonclaim to delete it, or use your shovel at a corner to resize it.", null); - this.addDefault(defaults, Messages.CreateClaimFailOverlapOtherPlayer, "You can't create a claim here because it would overlap {0}'s claim.", "0: other claim owner"); - this.addDefault(defaults, Messages.ClaimsDisabledWorld, "Land claims are disabled in this world.", null); - this.addDefault(defaults, Messages.ClaimStart, "Claim corner set! Use the shovel again at the opposite corner to claim a rectangle of land. To cancel, put your shovel away.", null); - this.addDefault(defaults, Messages.NewClaimTooNarrow, "This claim would be too small. Any claim must be at least {0} blocks wide.", "0: minimum claim width"); - this.addDefault(defaults, Messages.ResizeClaimInsufficientArea, "This claim would be too small. Any claim must use at least {0} total claim blocks.", "0: minimum claim area"); - this.addDefault(defaults, Messages.CreateClaimInsufficientBlocks, "You don't have enough blocks to claim that entire area. You need {0} more blocks.", "0: additional blocks needed"); - this.addDefault(defaults, Messages.AbandonClaimAdvertisement, "To delete another claim and free up some blocks, use /abandonclaim.", null); - this.addDefault(defaults, Messages.CreateClaimFailOverlapShort, "Your selected area overlaps an existing claim.", null); - this.addDefault(defaults, Messages.CreateClaimSuccess, "Claim created! Use /trust to share it with friends.", null); - this.addDefault(defaults, Messages.RescueAbortedMoved, "You moved! Rescue cancelled.", null); - this.addDefault(defaults, Messages.OnlyOwnersModifyClaims, "Only {0} can modify this claim.", "0: owner name"); - this.addDefault(defaults, Messages.NoBuildPvP, "You can't build in claims during PvP combat.", null); - this.addDefault(defaults, Messages.NoBuildPermission, "You don't have {0}'s permission to build here.", "0: owner name"); - this.addDefault(defaults, Messages.NoAccessPermission, "You don't have {0}'s permission to use that.", "0: owner name. access permission controls buttons, levers, and beds"); - this.addDefault(defaults, Messages.NoContainersPermission, "You don't have {0}'s permission to use that.", "0: owner's name. containers also include crafting blocks"); - this.addDefault(defaults, Messages.OwnerNameForAdminClaims, "an administrator", "as in 'You don't have an administrator's permission to build here.'"); - this.addDefault(defaults, Messages.UnknownPlayerName, "someone", "Name used for unknown players. UUID will be appended if available: \"someone (01234567-0123-0123-0123-0123456789ab)\""); - this.addDefault(defaults, Messages.ClaimTooSmallForEntities, "This claim isn't big enough for that. Try enlarging it.", null); - this.addDefault(defaults, Messages.TooManyEntitiesInClaim, "This claim has too many entities already. Try enlarging the claim or removing some animals, monsters, paintings, or minecarts.", null); - this.addDefault(defaults, Messages.YouHaveNoClaims, "You don't have any land claims.", null); - this.addDefault(defaults, Messages.ConfirmFluidRemoval, "Abandoning this claim will remove lava inside the claim. If you're sure, use /abandonclaim again.", null); - this.addDefault(defaults, Messages.AutoBanNotify, "Auto-banned {0}({1}). See logs for details.", null); - this.addDefault(defaults, Messages.AdjustGroupBlocksSuccess, "Adjusted bonus claim blocks for players with the {0} permission by {1}. New total: {2}.", "0: permission; 1: adjustment amount; 2: new total bonus"); - this.addDefault(defaults, Messages.InvalidPermissionID, "Please specify a player name, or a permission in [brackets].", null); - this.addDefault(defaults, Messages.HowToClaimRegex, "(^|.*\\W)how\\W.*\\W(claim|protect|lock)(\\W.*|$)", "This is a Java Regular Expression. Look it up before editing! It's used to tell players about the demo video when they ask how to claim land."); - this.addDefault(defaults, Messages.NoBuildOutsideClaims, "You can't build here unless you claim some land first.", null); - this.addDefault(defaults, Messages.PlayerOfflineTime, " Last login: {0} days ago.", "0: number of full days since last login"); - this.addDefault(defaults, Messages.BuildingOutsideClaims, "Other players can build here, too. Consider creating a land claim to protect your work!", null); - this.addDefault(defaults, Messages.TrappedWontWorkHere, "Sorry, unable to find a safe location to teleport you to. Contact an admin.", null); - this.addDefault(defaults, Messages.CommandBannedInPvP, "You can't use that command while in PvP combat.", null); - this.addDefault(defaults, Messages.UnclaimCleanupWarning, "The land you've unclaimed may be changed by other players or cleaned up by administrators. If you've built something there you want to keep, you should reclaim it.", null); - this.addDefault(defaults, Messages.NoTeleportPvPCombat, "You can't teleport while fighting another player.", null); - this.addDefault(defaults, Messages.NoTNTDamageAboveSeaLevel, "Warning: TNT will not destroy blocks above sea level.", null); - this.addDefault(defaults, Messages.NoTNTDamageClaims, "Warning: TNT will not destroy claimed blocks.", null); - this.addDefault(defaults, Messages.IgnoreClaimsAdvertisement, "To override, use /ignoreclaims.", null); - this.addDefault(defaults, Messages.NoPermissionForCommand, "You don't have permission to do that.", null); - this.addDefault(defaults, Messages.ClaimsListNoPermission, "You don't have permission to get information about another player's land claims.", null); - this.addDefault(defaults, Messages.ExplosivesDisabled, "This claim is now protected from explosions. Use /claimexplosions again to disable.", null); - this.addDefault(defaults, Messages.ExplosivesEnabled, "This claim is now vulnerable to explosions. Use /claimexplosions again to re-enable protections.", null); - this.addDefault(defaults, Messages.ClaimExplosivesAdvertisement, "To allow explosives to destroy blocks in this land claim, use /claimexplosions.", null); - this.addDefault(defaults, Messages.PlayerInPvPSafeZone, "That player is in a PvP safe zone.", null); - this.addDefault(defaults, Messages.NoPistonsOutsideClaims, "Warning: Pistons won't move blocks outside land claims.", null); - this.addDefault(defaults, Messages.SoftMuted, "Soft-muted {0}.", "0: The changed player's name."); - this.addDefault(defaults, Messages.UnSoftMuted, "Un-soft-muted {0}.", "0: The changed player's name."); - this.addDefault(defaults, Messages.DropUnlockAdvertisement, "Other players can't pick up your dropped items unless you /unlockdrops first.", null); - this.addDefault(defaults, Messages.PickupBlockedExplanation, "You can't pick this up unless {0} uses /unlockdrops.", "0: The item stack's owner."); - this.addDefault(defaults, Messages.DropUnlockConfirmation, "Unlocked your drops. Other players may now pick them up (until you die again).", null); - this.addDefault(defaults, Messages.DropUnlockOthersConfirmation, "Unlocked {0}'s drops.", "0: The owner of the unlocked drops."); - this.addDefault(defaults, Messages.AdvertiseACandACB, "You may use /acb to give yourself more claim blocks, or /adminclaims to create a free administrative claim.", null); - this.addDefault(defaults, Messages.AdvertiseAdminClaims, "You could create an administrative land claim instead using /adminclaims, which you'd share with other administrators.", null); - this.addDefault(defaults, Messages.AdvertiseACB, "You may use /acb to give yourself more claim blocks.", null); - this.addDefault(defaults, Messages.NotYourPet, "That belongs to {0} until it's given to you with /givepet.", "0: owner name"); - this.addDefault(defaults, Messages.PetGiveawayConfirmation, "Pet transferred.", null); - this.addDefault(defaults, Messages.PetTransferCancellation, "Pet giveaway cancelled.", null); - this.addDefault(defaults, Messages.ReadyToTransferPet, "Ready to transfer! Right-click the pet you'd like to give away, or cancel with /givepet cancel.", null); - this.addDefault(defaults, Messages.AvoidGriefClaimLand, "Prevent grief! If you claim your land, you will be grief-proof.", null); - this.addDefault(defaults, Messages.BecomeMayor, "Subdivide your land claim and become a mayor!", null); - this.addDefault(defaults, Messages.ClaimCreationFailedOverClaimCountLimit, "You've reached your limit on land claims. Use /abandonclaim to remove one before creating another.", null); - this.addDefault(defaults, Messages.CreateClaimFailOverlapRegion, "You can't claim all of this because you're not allowed to build here.", null); - this.addDefault(defaults, Messages.ResizeFailOverlapRegion, "You don't have permission to build there, so you can't claim that area.", null); - this.addDefault(defaults, Messages.ShowNearbyClaims, "Found {0} land claims.", "0: Number of claims found."); - this.addDefault(defaults, Messages.NoChatUntilMove, "Sorry, but you have to move a little more before you can chat. We get lots of spam bots here. :)", null); - this.addDefault(defaults, Messages.SetClaimBlocksSuccess, "Updated accrued claim blocks.", null); - this.addDefault(defaults, Messages.IgnoreConfirmation, "You're now ignoring chat messages from that player.", null); - this.addDefault(defaults, Messages.UnIgnoreConfirmation, "You're no longer ignoring chat messages from that player.", null); - this.addDefault(defaults, Messages.NotIgnoringPlayer, "You're not ignoring that player.", null); - this.addDefault(defaults, Messages.SeparateConfirmation, "Those players will now ignore each other in chat.", null); - this.addDefault(defaults, Messages.UnSeparateConfirmation, "Those players will no longer ignore each other in chat.", null); - this.addDefault(defaults, Messages.NotIgnoringAnyone, "You're not ignoring anyone.", null); - this.addDefault(defaults, Messages.TrustListHeader, "Explicit permissions here:", "0: The claim's owner"); - this.addDefault(defaults, Messages.Manage, "Manage", null); - this.addDefault(defaults, Messages.Build, "Build", null); - this.addDefault(defaults, Messages.Containers, "Containers", null); - this.addDefault(defaults, Messages.Access, "Access", null); - this.addDefault(defaults, Messages.HasSubclaimRestriction, "This subclaim does not inherit permissions from the parent", null); - this.addDefault(defaults, Messages.StartBlockMath, "{0} blocks from play + {1} bonus = {2} total.", null); - this.addDefault(defaults, Messages.ClaimsListHeader, "Claims:", null); - this.addDefault(defaults, Messages.ContinueBlockMath, " (-{0} blocks)", null); - this.addDefault(defaults, Messages.EndBlockMath, " = {0} blocks left to spend", null); - this.addDefault(defaults, Messages.NoClaimDuringPvP, "You can't claim lands during PvP combat.", null); - this.addDefault(defaults, Messages.UntrustAllOwnerOnly, "Only the claim owner can clear all its permissions.", null); - this.addDefault(defaults, Messages.ManagersDontUntrustManagers, "Only the claim owner can demote a manager.", null); - this.addDefault(defaults, Messages.PlayerNotIgnorable, "You can't ignore that player.", null); - this.addDefault(defaults, Messages.NoEnoughBlocksForChestClaim, "Because you don't have any claim blocks available, no automatic land claim was created for you. You can use /claimslist to monitor your available claim block total.", null); - this.addDefault(defaults, Messages.MustHoldModificationToolForThat, "You must be holding a golden shovel to do that.", null); - this.addDefault(defaults, Messages.StandInClaimToResize, "Stand inside the land claim you want to resize.", null); - this.addDefault(defaults, Messages.ClaimsExtendToSky, "Land claims always extend to max build height.", null); - this.addDefault(defaults, Messages.ClaimsAutoExtendDownward, "Land claims auto-extend deeper into the ground when you place blocks under them.", null); - this.addDefault(defaults, Messages.MinimumRadius, "Minimum radius is {0}.", "0: minimum radius"); - this.addDefault(defaults, Messages.RadiusRequiresGoldenShovel, "You must be holding a golden shovel when specifying a radius.", null); - this.addDefault(defaults, Messages.ClaimTooSmallForActiveBlocks, "This claim isn't big enough to support any active block types (hoppers, spawners, beacons...). Make the claim bigger first.", null); - this.addDefault(defaults, Messages.TooManyActiveBlocksInClaim, "This claim is at its limit for active block types (hoppers, spawners, beacons...). Either make it bigger, or remove other active blocks first.", null); - - this.addDefault(defaults, Messages.BookAuthor, "BigScary", null); - this.addDefault(defaults, Messages.BookTitle, "How to Claim Land", null); - this.addDefault(defaults, Messages.BookLink, "Click: {0}", "{0}: video URL"); - this.addDefault(defaults, Messages.BookIntro, "Claim land to protect your stuff! Click the link above to learn land claims in 3 minutes or less. :)", null); - this.addDefault(defaults, Messages.BookTools, "Our claim tools are {0} and {1}.", "0: claim modification tool name; 1:claim information tool name"); - this.addDefault(defaults, Messages.BookDisabledChestClaims, " On this server, placing a chest will NOT claim land for you.", null); - this.addDefault(defaults, Messages.BookUsefulCommands, "Useful Commands:", null); - this.addDefault(defaults, Messages.NoProfanity, "Please moderate your language.", null); - this.addDefault(defaults, Messages.IsIgnoringYou, "That player is ignoring you.", null); - this.addDefault(defaults, Messages.ConsoleOnlyCommand, "That command may only be executed from the server console.", null); - this.addDefault(defaults, Messages.WorldNotFound, "World not found.", null); - this.addDefault(defaults, Messages.TooMuchIpOverlap, "Sorry, there are too many players logged in with your IP address.", null); - - this.addDefault(defaults, Messages.StandInSubclaim, "You need to be standing in a subclaim to restrict it", null); - this.addDefault(defaults, Messages.SubclaimRestricted, "This subclaim's permissions will no longer inherit from the parent claim", null); - this.addDefault(defaults, Messages.SubclaimUnrestricted, "This subclaim's permissions will now inherit from the parent claim", null); - - this.addDefault(defaults, Messages.NetherPortalTrapDetectionMessage, "It seems you might be stuck inside a nether portal. We will rescue you in a few seconds if that is the case!", "Sent to player on join, if they left while inside a nether portal."); + this.messages = new String[messageIDs.length]; //load the config file FileConfiguration config = YamlConfiguration.loadConfiguration(new File(messagesFilePath)); //for each message ID - for (Messages messageID : messageIDs) + for (Messages message : messageIDs) { - //get default for this message - CustomizableMessage messageData = defaults.get(messageID.name()); - - //if default is missing, log an error and use some fake data for now so that the plugin can run - if (messageData == null) + String messagePath = "Messages." + message.name(); + // If available, migrate legacy path. + if (config.isString(messagePath + ".Text")) { - GriefPrevention.AddLogEntry("Missing message for " + messageID.name() + ". Please contact the developer."); - messageData = new CustomizableMessage(messageID, "Missing message! ID: " + messageID.name() + ". Please contact a server admin.", null); + this.messages[message.ordinal()] = config.getString(messagePath + ".Text", message.defaultValue); } - - //read the message from the file, use default if necessary - this.messages[messageID.ordinal()] = config.getString("Messages." + messageID.name() + ".Text", messageData.text); - config.set("Messages." + messageID.name() + ".Text", this.messages[messageID.ordinal()]); + // Otherwise prefer current value if available. + else + { + this.messages[message.ordinal()] = config.getString(messagePath, message.defaultValue); + } + config.set(messagePath, this.messages[message.ordinal()]); //support color codes - if (messageID != Messages.HowToClaimRegex) + if (message != Messages.HowToClaimRegex) { - this.messages[messageID.ordinal()] = this.messages[messageID.ordinal()].replace('$', (char) 0x00A7); + this.messages[message.ordinal()] = this.messages[message.ordinal()].replace('$', (char) 0x00A7); } - if (messageData.notes != null) + if (message.notes != null) { - messageData.notes = config.getString("Messages." + messageID.name() + ".Notes", messageData.notes); - config.set("Messages." + messageID.name() + ".Notes", messageData.notes); + // Import old non-comment notes. + String notesString = config.getString(messagePath + ".Notes", message.notes); + // Import existing comment notes. + List notes = config.getComments(messagePath); + if (notes.isEmpty()) { + notes = List.of(notesString); + } + config.setComments(messagePath, notes); } } //save any changes try { - config.options().header("Use a YAML editor like NotepadPlusPlus to edit this file. \nAfter editing, back up your changes before reloading the server in case you made a syntax error. \nUse dollar signs ($) for formatting codes, which are documented here: http://minecraft.gamepedia.com/Formatting_codes"); + config.options().setHeader(List.of( + "Use a YAML editor like NotepadPlusPlus to edit this file.", + "After editing, back up your changes before reloading the server in case you made a syntax error.", + "Use dollar signs ($) for formatting codes, which are documented here: http://minecraft.wiki/Formatting_codes#Color_codes" + )); config.save(DataStore.messagesFilePath); } catch (IOException exception) { GriefPrevention.AddLogEntry("Unable to write to the configuration file at \"" + DataStore.messagesFilePath + "\""); } - - defaults.clear(); - System.gc(); - } - - private void addDefault(HashMap defaults, - Messages id, String text, String notes) - { - CustomizableMessage message = new CustomizableMessage(id, text, notes); - defaults.put(id.name(), message); } synchronized public String getMessage(Messages messageID, String... args) diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/EntityDamageHandler.java b/src/main/java/me/ryanhamshire/GriefPrevention/EntityDamageHandler.java index 0a5e6ccf..56a7d025 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/EntityDamageHandler.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/EntityDamageHandler.java @@ -45,6 +45,7 @@ import org.bukkit.persistence.PersistentDataType; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; +import org.bukkit.potion.PotionEffectTypeCategory; import org.bukkit.projectiles.ProjectileSource; import org.bukkit.util.Vector; import org.jetbrains.annotations.NotNull; @@ -63,34 +64,13 @@ public class EntityDamageHandler implements Listener private static final Set GRIEF_EFFECTS = Set.of( // Damaging effects - PotionEffectType.HARM, + PotionEffectType.INSTANT_DAMAGE, PotionEffectType.POISON, PotionEffectType.WITHER, // Effects that could remove entities from normally-secure pens - PotionEffectType.JUMP, + PotionEffectType.JUMP_BOOST, PotionEffectType.LEVITATION ); - private static final Set POSITIVE_EFFECTS = Set.of( - PotionEffectType.ABSORPTION, - PotionEffectType.CONDUIT_POWER, - PotionEffectType.DAMAGE_RESISTANCE, - PotionEffectType.DOLPHINS_GRACE, - PotionEffectType.FAST_DIGGING, - PotionEffectType.FIRE_RESISTANCE, - PotionEffectType.HEAL, - PotionEffectType.HEALTH_BOOST, - PotionEffectType.HERO_OF_THE_VILLAGE, - PotionEffectType.INCREASE_DAMAGE, - PotionEffectType.INVISIBILITY, - PotionEffectType.JUMP, - PotionEffectType.LUCK, - PotionEffectType.NIGHT_VISION, - PotionEffectType.REGENERATION, - PotionEffectType.SATURATION, - PotionEffectType.SLOW_FALLING, - PotionEffectType.SPEED, - PotionEffectType.WATER_BREATHING - ); private static final Set TEMPTABLE_SEMI_HOSTILES = Set.of( EntityType.HOGLIN, EntityType.POLAR_BEAR, @@ -150,7 +130,7 @@ private void handleEntityDamageEvent(@NotNull EntityDamageInstance event, boolea if (event.damaged() instanceof Mule && !instance.config_claims_protectDonkeys) return; if (event.damaged() instanceof Llama && !instance.config_claims_protectLlamas) return; //protected death loot can't be destroyed, only picked up or despawned due to expiration - if (event.damaged().getType() == EntityType.DROPPED_ITEM) + if (event.damaged().getType() == EntityType.ITEM) { if (event.damaged().hasMetadata("GP_ITEMOWNER")) { @@ -582,7 +562,7 @@ private boolean handleClaimedBuildTrustDamageByEntity( && entityType != EntityType.GLOW_ITEM_FRAME && entityType != EntityType.ARMOR_STAND && entityType != EntityType.VILLAGER - && entityType != EntityType.ENDER_CRYSTAL) + && entityType != EntityType.END_CRYSTAL) { return false; } @@ -663,7 +643,7 @@ private boolean handleCreatureDamageByEntity( if (attacker == null && damageSourceType != EntityType.CREEPER && damageSourceType != EntityType.WITHER - && damageSourceType != EntityType.ENDER_CRYSTAL + && damageSourceType != EntityType.END_CRYSTAL && damageSourceType != EntityType.AREA_EFFECT_CLOUD && damageSourceType != EntityType.WITCH && !(damageSource instanceof Projectile) @@ -699,7 +679,7 @@ private boolean handleCreatureDamageByEntity( playerData.lastClaim = claim; // Do not message players about fireworks to prevent spam due to multi-hits. - sendMessages &= damageSourceType != EntityType.FIREWORK; + sendMessages &= damageSourceType != EntityType.FIREWORK_ROCKET; Supplier override = null; if (sendMessages) @@ -880,7 +860,7 @@ else if (damageSource instanceof Projectile projectile) } //if not a player and not an explosion, always allow - if (attacker == null && damageSourceType != EntityType.CREEPER && damageSourceType != EntityType.WITHER && damageSourceType != EntityType.PRIMED_TNT) + if (attacker == null && damageSourceType != EntityType.CREEPER && damageSourceType != EntityType.WITHER && damageSourceType != EntityType.TNT) { return; } @@ -999,7 +979,7 @@ public void onPotionSplash(@NotNull PotionSplashEvent event) if (thrower == null) return; //otherwise, no restrictions for positive effects - if (POSITIVE_EFFECTS.contains(effectType)) continue; + if (effectType.getCategory() == PotionEffectTypeCategory.BENEFICIAL) continue; for (LivingEntity affected : event.getAffectedEntities()) { diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/EntityEventHandler.java b/src/main/java/me/ryanhamshire/GriefPrevention/EntityEventHandler.java index 2ff26678..8fb4c460 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/EntityEventHandler.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/EntityEventHandler.java @@ -18,8 +18,10 @@ package me.ryanhamshire.GriefPrevention; +import com.griefprevention.protection.ProtectionHelper; import me.ryanhamshire.GriefPrevention.events.ProtectDeathDropsEvent; import org.bukkit.Bukkit; +import org.bukkit.ExplosionResult; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.OfflinePlayer; @@ -36,10 +38,12 @@ import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; import org.bukkit.entity.Vehicle; +import org.bukkit.event.Event; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockExplodeEvent; +import org.bukkit.event.block.CauldronLevelChangeEvent; import org.bukkit.event.block.EntityBlockFormEvent; import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; @@ -89,14 +93,10 @@ public EntityEventHandler(DataStore dataStore, GriefPrevention plugin) public void onEntityFormBlock(EntityBlockFormEvent event) { Entity entity = event.getEntity(); - if (entity.getType() == EntityType.PLAYER) + if (entity instanceof Player player + && ProtectionHelper.checkPermission(player, event.getBlock().getLocation(), ClaimPermission.Build, event) != null) { - Player player = (Player) event.getEntity(); - String noBuildReason = GriefPrevention.instance.allowBuild(player, event.getBlock().getLocation(), event.getNewState().getType()); - if (noBuildReason != null) - { - event.setCancelled(true); - } + event.setCancelled(true); } } @@ -148,15 +148,14 @@ else if (event.getEntityType() == EntityType.WITHER) //don't allow crops to be trampled, except by a player with build permission else if (event.getTo() == Material.DIRT && event.getBlock().getType() == Material.FARMLAND) { - if (event.getEntityType() != EntityType.PLAYER) + if (!(event.getEntity() instanceof Player player)) { event.setCancelled(true); } else { - Player player = (Player) event.getEntity(); Block block = event.getBlock(); - if (GriefPrevention.instance.allowBreak(player, block, block.getLocation()) != null) + if (ProtectionHelper.checkPermission(player, block.getLocation(), ClaimPermission.Build, event) != null) { event.setCancelled(true); } @@ -173,10 +172,10 @@ else if (event.getBlock().getType() == Material.POWDER_SNOW && event.getTo() == else if (event.getEntity() instanceof Vehicle && !event.getEntity().getPassengers().isEmpty()) { Entity driver = event.getEntity().getPassengers().get(0); - if (driver instanceof Player) + if (driver instanceof Player player) { Block block = event.getBlock(); - if (GriefPrevention.instance.allowBreak((Player) driver, block, block.getLocation()) != null) + if (ProtectionHelper.checkPermission(player, block.getLocation(), ClaimPermission.Build, event) != null) { event.setCancelled(true); } @@ -327,7 +326,7 @@ else if (mob.isLeashed() && mob.getLeashHolder() instanceof Player localPlayer) if (player != null) { Block block = event.getBlock(); - if (GriefPrevention.instance.allowBreak(player, block, block.getLocation()) != null) + if (ProtectionHelper.checkPermission(player, block.getLocation(), ClaimPermission.Build, event) != null) { event.setCancelled(true); } @@ -379,23 +378,99 @@ public void onEntityInteract(EntityInteractEvent event) @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) public void onEntityExplode(EntityExplodeEvent explodeEvent) { - this.handleExplosion(explodeEvent.getLocation(), explodeEvent.getEntity(), explodeEvent.blockList()); + // If there aren't any affected blocks, there's nothing to do. Vanilla mob griefing rule causes this. + if (explodeEvent.blockList().isEmpty()) return; + + // Explosion causes interactable blocks (levers, buttons, etc.) to change state. + if (explodeEvent.getExplosionResult() == ExplosionResult.TRIGGER_BLOCK) + { + handleExplodeInteract(explodeEvent.getLocation(), explodeEvent.getEntity(), explodeEvent.blockList(), explodeEvent); + } + // Explosion damages world. + else + { + handleExplosion(explodeEvent.getLocation(), explodeEvent.getEntity(), explodeEvent.blockList()); + } } //when a block explodes... @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) public void onBlockExplode(BlockExplodeEvent explodeEvent) { - this.handleExplosion(explodeEvent.getBlock().getLocation(), null, explodeEvent.blockList()); + // If there aren't any affected blocks, there's nothing to do. Vanilla mob griefing rule causes this. + if (explodeEvent.blockList().isEmpty()) return; + + // Explosion causes interactable blocks (levers, buttons, etc.) to change state. + if (explodeEvent.getExplosionResult() == ExplosionResult.TRIGGER_BLOCK) + { + handleExplodeInteract(explodeEvent.getBlock().getLocation(), null, explodeEvent.blockList(), explodeEvent); + } + // Explosion damages world. + else + { + handleExplosion(explodeEvent.getBlock().getLocation(), null, explodeEvent.blockList()); + } } + void handleExplodeInteract(@NotNull Location location, @Nullable Entity entity, @NotNull List blocks, @NotNull Event event) + { + World world = location.getWorld(); + + if (world == null || !GriefPrevention.instance.claimsEnabledForWorld(world)) return; + + Player player = null; + PlayerData playerData = null; + ProjectileSource source = null; + if (entity instanceof Projectile projectile) + { + source = projectile.getShooter(); + if (source instanceof Player) + { + player = (Player) source; + playerData = dataStore.getPlayerData(player.getUniqueId()); + } + } + + List removed = new ArrayList<>(); + Claim cachedClaim = playerData != null ? playerData.lastClaim : null; + + for (Block block : blocks) + { + // Always ignore air blocks. + if (block.getType().isAir()) continue; + + Claim claim = this.dataStore.getClaimAt(block.getLocation(), false, cachedClaim); + + // Is it in a land claim? + if (claim == null) continue; + + cachedClaim = claim; + + if (player == null) + { + // If the source is not part of the claim, prevent interaction. + if (!isBlockSourceInClaim(source, claim)) + removed.add(block); + continue; + } + + // If the player is not allowed to interact with blocks, prevent interaction. + if (claim.checkPermission(player, ClaimPermission.Access, event) != null) + removed.add(block); + } + + if (playerData != null && cachedClaim != null) + playerData.lastClaim = cachedClaim; + + blocks.removeAll(removed); + } - void handleExplosion(Location location, Entity entity, List blocks) + void handleExplosion(@NotNull Location location, @Nullable Entity entity, @NotNull List blocks) { //only applies to claims-enabled worlds World world = location.getWorld(); - if (!GriefPrevention.instance.claimsEnabledForWorld(world)) return; + if (world == null || !GriefPrevention.instance.claimsEnabledForWorld(world)) return; //FEATURE: explosions don't destroy surface blocks by default boolean isCreeper = (entity != null && entity.getType() == EntityType.CREEPER); @@ -407,8 +482,6 @@ void handleExplosion(Location location, Entity entity, List blocks) { for (int i = 0; i < blocks.size(); i++) { - Block block = blocks.get(i); - blocks.remove(i--); } @@ -421,7 +494,7 @@ void handleExplosion(Location location, Entity entity, List blocks) for (Block block : blocks) { //always ignore air blocks - if (block.getType() == Material.AIR) continue; + if (block.getType().isAir()) continue; //is it in a land claim? Claim claim = this.dataStore.getClaimAt(block.getLocation(), false, cachedClaim); @@ -652,19 +725,18 @@ public void onHangingBreak(HangingBreakEvent event) Entity remover = entityEvent.getRemover(); //again, making sure the breaker is a player - if (remover.getType() != EntityType.PLAYER) + if (!(remover instanceof Player playerRemover)) { event.setCancelled(true); return; } //if the player doesn't have build permission, don't allow the breakage - Player playerRemover = (Player) entityEvent.getRemover(); - String noBuildReason = GriefPrevention.instance.allowBuild(playerRemover, event.getEntity().getLocation(), Material.AIR); + Supplier noBuildReason = ProtectionHelper.checkPermission(playerRemover, event.getEntity().getLocation(), ClaimPermission.Build, event); if (noBuildReason != null) { event.setCancelled(true); - GriefPrevention.sendMessage(playerRemover, TextMode.Err, noBuildReason); + GriefPrevention.sendMessage(playerRemover, TextMode.Err, noBuildReason.get()); } } @@ -674,15 +746,16 @@ public void onPaintingPlace(HangingPlaceEvent event) { //don't track in worlds where claims are not enabled if (!GriefPrevention.instance.claimsEnabledForWorld(event.getBlock().getWorld())) return; + if (event.getPlayer() == null) return; //FEATURE: similar to above, placing a painting requires build permission in the claim //if the player doesn't have permission, don't allow the placement - String noBuildReason = GriefPrevention.instance.allowBuild(event.getPlayer(), event.getEntity().getLocation(), Material.PAINTING); + Supplier noBuildReason = ProtectionHelper.checkPermission(event.getPlayer(), event.getEntity().getLocation(), ClaimPermission.Build, event); if (noBuildReason != null) { event.setCancelled(true); - GriefPrevention.sendMessage(event.getPlayer(), TextMode.Err, noBuildReason); + GriefPrevention.sendMessage(event.getPlayer(), TextMode.Err, noBuildReason.get()); return; } } @@ -706,6 +779,26 @@ public void onEntityPickUpItem(@NotNull EntityPickupItemEvent event) preventPvpSpawnCamp(event, player); } + @EventHandler + public void onCauldron(@NotNull CauldronLevelChangeEvent event) + { + //don't track in worlds where claims are not enabled + if (!GriefPrevention.instance.claimsEnabledForWorld(event.getBlock().getWorld())) return; + + // Check if the entity is a player + Entity entity = event.getEntity(); + if (entity == null) return; + if (!(entity instanceof Player player)) return; + + //if the player doesn't have build permission, don't allow the interaction + Supplier noBuildReason = ProtectionHelper.checkPermission(player, player.getLocation(), ClaimPermission.Build, event); + if (noBuildReason != null) + { + event.setCancelled(true); + GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason.get()); + } + } + private void protectLockedDrops(@NotNull EntityPickupItemEvent event, @Nullable Player player) { Item item = event.getItem(); diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/GriefPrevention.java b/src/main/java/me/ryanhamshire/GriefPrevention/GriefPrevention.java index c5f05a10..cfe9f6f3 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/GriefPrevention.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/GriefPrevention.java @@ -22,10 +22,10 @@ import com.google.common.cache.CacheBuilder; import com.griefprevention.commands.ClaimCommand; import com.griefprevention.metrics.MetricsHandler; +import com.griefprevention.protection.ProtectionHelper; import ltd.lemongaming.lgcore.libs.com.tcoded.folialib.FoliaLib; import ltd.lemongaming.lgcore.libs.com.tcoded.folialib.wrapper.WrappedTask; import me.ryanhamshire.GriefPrevention.DataStore.NoTransferException; -import me.ryanhamshire.GriefPrevention.events.PreventBlockBreakEvent; import me.ryanhamshire.GriefPrevention.events.SaveTrappedPlayerEvent; import me.ryanhamshire.GriefPrevention.events.TrustChangedEvent; import org.bukkit.BanList; @@ -33,7 +33,6 @@ import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Chunk; -import org.bukkit.FluidCollisionMode; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; @@ -56,11 +55,15 @@ import org.bukkit.permissions.PermissionAttachmentInfo; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.profile.PlayerProfile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Date; @@ -68,12 +71,14 @@ import java.util.HashSet; import java.util.List; import java.util.Map.Entry; +import java.util.Properties; import java.util.Set; import java.util.UUID; import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; +import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -148,8 +153,6 @@ public class GriefPrevention extends JavaPlugin public int config_claims_minArea; //minimum area for non-admin claims public int config_claims_chestClaimExpirationDays; //number of days of inactivity before an automatic chest claim will be deleted - public int config_claims_unusedClaimExpirationDays; //number of days of inactivity before an unused (nothing build) claim will be deleted - public boolean config_claims_survivalAutoNatureRestoration; //whether survival claims will be automatically restored to nature when auto-deleted public boolean config_claims_allowTrappedInAdminClaims; //whether it should be allowed to use /trapped in adminclaims. public Material config_claims_investigationTool; //which material will be used to investigate claims with a right click @@ -577,11 +580,9 @@ else if (world.getEnvironment() == Environment.NORMAL) AddLogEntry("Updated default value for GriefPrevention.Claims.MaximumDepth to " + Integer.MIN_VALUE); } this.config_claims_chestClaimExpirationDays = config.getInt("GriefPrevention.Claims.Expiration.ChestClaimDays", 7); - this.config_claims_unusedClaimExpirationDays = config.getInt("GriefPrevention.Claims.Expiration.UnusedClaimDays", 14); this.config_claims_expirationDays = config.getInt("GriefPrevention.Claims.Expiration.AllClaims.DaysInactive", 60); this.config_claims_expirationExemptionTotalBlocks = config.getInt("GriefPrevention.Claims.Expiration.AllClaims.ExceptWhenOwnerHasTotalClaimBlocks", 10000); this.config_claims_expirationExemptionBonusBlocks = config.getInt("GriefPrevention.Claims.Expiration.AllClaims.ExceptWhenOwnerHasBonusClaimBlocks", 5000); - this.config_claims_survivalAutoNatureRestoration = config.getBoolean("GriefPrevention.Claims.Expiration.AutomaticNatureRestoration.SurvivalWorlds", false); this.config_claims_allowTrappedInAdminClaims = config.getBoolean("GriefPrevention.Claims.AllowTrappedInAdminClaims", false); this.config_claims_maxClaimsPerPlayer = config.getInt("GriefPrevention.Claims.MaximumNumberOfClaimsPerPlayer", 0); @@ -689,9 +690,7 @@ else if (world.getEnvironment() == Environment.NORMAL) this.config_pvp_protectPets = config.getBoolean("GriefPrevention.PvP.ProtectPetsOutsideLandClaims", false); //optional database settings - this.databaseUrl = config.getString("GriefPrevention.Database.URL", ""); - this.databaseUserName = config.getString("GriefPrevention.Database.UserName", ""); - this.databasePassword = config.getString("GriefPrevention.Database.Password", ""); + loadDatabaseSettings(config); this.config_advanced_fixNegativeClaimblockAmounts = config.getBoolean("GriefPrevention.Advanced.fixNegativeClaimblockAmounts", true); this.config_advanced_claim_expiration_check_rate = config.getInt("GriefPrevention.Advanced.ClaimExpirationCheckRate", 60); @@ -741,11 +740,9 @@ else if (world.getEnvironment() == Environment.NORMAL) outConfig.set("GriefPrevention.Claims.InvestigationTool", this.config_claims_investigationTool.name()); outConfig.set("GriefPrevention.Claims.ModificationTool", this.config_claims_modificationTool.name()); outConfig.set("GriefPrevention.Claims.Expiration.ChestClaimDays", this.config_claims_chestClaimExpirationDays); - outConfig.set("GriefPrevention.Claims.Expiration.UnusedClaimDays", this.config_claims_unusedClaimExpirationDays); outConfig.set("GriefPrevention.Claims.Expiration.AllClaims.DaysInactive", this.config_claims_expirationDays); outConfig.set("GriefPrevention.Claims.Expiration.AllClaims.ExceptWhenOwnerHasTotalClaimBlocks", this.config_claims_expirationExemptionTotalBlocks); outConfig.set("GriefPrevention.Claims.Expiration.AllClaims.ExceptWhenOwnerHasBonusClaimBlocks", this.config_claims_expirationExemptionBonusBlocks); - outConfig.set("GriefPrevention.Claims.Expiration.AutomaticNatureRestoration.SurvivalWorlds", this.config_claims_survivalAutoNatureRestoration); outConfig.set("GriefPrevention.Claims.AllowTrappedInAdminClaims", this.config_claims_allowTrappedInAdminClaims); outConfig.set("GriefPrevention.Claims.MaximumNumberOfClaimsPerPlayer", this.config_claims_maxClaimsPerPlayer); outConfig.set("GriefPrevention.Claims.VillagerTradingRequiresPermission", this.config_claims_villagerTradingRequiresTrust); @@ -820,10 +817,6 @@ else if (world.getEnvironment() == Environment.NORMAL) outConfig.set("GriefPrevention.HardModeZombiesBreakDoors", this.config_zombiesBreakDoors); outConfig.set("GriefPrevention.MobProjectilesChangeBlocks", this.config_mobProjectilesChangeBlocks); - outConfig.set("GriefPrevention.Database.URL", this.databaseUrl); - outConfig.set("GriefPrevention.Database.UserName", this.databaseUserName); - outConfig.set("GriefPrevention.Database.Password", this.databasePassword); - outConfig.set("GriefPrevention.UseBanCommand", this.config_ban_useCommand); outConfig.set("GriefPrevention.BanCommandPattern", this.config_ban_commandFormat); @@ -885,6 +878,59 @@ else if (world.getEnvironment() == Environment.NORMAL) } } + private void loadDatabaseSettings(@NotNull FileConfiguration legacyConfig) + { + File databasePropsFile = new File(DataStore.dataLayerFolderPath, "database.properties"); + Properties databaseProps = new Properties(); + + // If properties file exists, use it - old config has already been migrated. + if (databasePropsFile.exists() && databasePropsFile.isFile()) + { + try (FileReader reader = new FileReader(databasePropsFile, StandardCharsets.UTF_8)) + { + // Load properties from file. + databaseProps.load(reader); + + // Set values from loaded properties. + databaseUrl = databaseProps.getProperty("jdbcUrl", ""); + databaseUserName = databaseProps.getProperty("username", ""); + databasePassword = databaseProps.getProperty("password", ""); + } + catch (IOException e) + { + getLogger().log(Level.SEVERE, "Unable to read database.properties", e); + } + + return; + } + + // Otherwise, database details may not have been migrated from legacy configuration. + // Try to load them. + databaseUrl = legacyConfig.getString("GriefPrevention.Database.URL", ""); + databaseUserName = legacyConfig.getString("GriefPrevention.Database.UserName", ""); + databasePassword = legacyConfig.getString("GriefPrevention.Database.Password", ""); + + // If not in use already, database settings are "secret" to discourage adoption until datastore is rewritten. + if (databaseUrl.isBlank()) { + return; + } + + // Set properties to loaded values. + databaseProps.setProperty("jdbcUrl", databaseUrl); + databaseProps.setProperty("username", databaseUserName); + databaseProps.setProperty("password", databasePassword); + + // Write properties file for future usage. + try (FileWriter writer = new FileWriter(databasePropsFile, StandardCharsets.UTF_8)) + { + databaseProps.store(writer, null); + } + catch (IOException e) + { + getLogger().log(Level.SEVERE, "Unable to write database.properties", e); + } + } + private ClaimsMode configStringToClaimsMode(String configSetting) { if (configSetting.equalsIgnoreCase("Survival")) @@ -1140,50 +1186,6 @@ else if (cmd.getName().equalsIgnoreCase("abandonallclaims") && player != null) return true; } - //restore nature - else if (cmd.getName().equalsIgnoreCase("restorenature") && player != null) - { - //change shovel mode - PlayerData playerData = this.dataStore.getPlayerData(player.getUniqueId()); - playerData.shovelMode = ShovelMode.RestoreNature; - GriefPrevention.sendMessage(player, TextMode.Instr, Messages.RestoreNatureActivate); - return true; - } - - //restore nature aggressive mode - else if (cmd.getName().equalsIgnoreCase("restorenatureaggressive") && player != null) - { - //change shovel mode - PlayerData playerData = this.dataStore.getPlayerData(player.getUniqueId()); - playerData.shovelMode = ShovelMode.RestoreNatureAggressive; - GriefPrevention.sendMessage(player, TextMode.Warn, Messages.RestoreNatureAggressiveActivate); - return true; - } - - //restore nature fill mode - else if (cmd.getName().equalsIgnoreCase("restorenaturefill") && player != null) - { - //change shovel mode - PlayerData playerData = this.dataStore.getPlayerData(player.getUniqueId()); - playerData.shovelMode = ShovelMode.RestoreNatureFill; - - //set radius based on arguments - playerData.fillRadius = 2; - if (args.length > 0) - { - try - { - playerData.fillRadius = Integer.parseInt(args[0]); - } - catch (Exception exception) { } - } - - if (playerData.fillRadius < 0) playerData.fillRadius = 2; - - GriefPrevention.sendMessage(player, TextMode.Success, Messages.FillModeActive, String.valueOf(playerData.fillRadius)); - return true; - } - //trust else if (cmd.getName().equalsIgnoreCase("trust") && player != null) { @@ -1638,15 +1640,8 @@ else if (cmd.getName().equalsIgnoreCase("deleteclaim") && player != null) } else { - claim.removeSurfaceFluids(null); this.dataStore.deleteClaim(claim, true, true); - //if in a creative mode world, /restorenature the claim - if (GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()) || GriefPrevention.instance.config_claims_survivalAutoNatureRestoration) - { - GriefPrevention.instance.restoreClaim(claim, 0); - } - GriefPrevention.sendMessage(player, TextMode.Success, Messages.DeleteSuccess); GriefPrevention.AddLogEntry(player.getName() + " deleted " + claim.getOwnerName() + "'s claim at " + GriefPrevention.getfriendlyLocationString(claim.getLesserBoundaryCorner()), CustomLogEntryTypes.AdminActivity); @@ -2160,54 +2155,6 @@ else if (cmd.getName().equalsIgnoreCase("gpreload")) return true; } - //givepet - else if (cmd.getName().equalsIgnoreCase("givepet") && player != null) - { - //requires one parameter - if (args.length < 1) return false; - - PlayerData playerData = this.dataStore.getPlayerData(player.getUniqueId()); - - //special case: cancellation - if (args[0].equalsIgnoreCase("cancel")) - { - playerData.petGiveawayRecipient = null; - GriefPrevention.sendMessage(player, TextMode.Success, Messages.PetTransferCancellation); - return true; - } - - //find the specified player - OfflinePlayer targetPlayer = this.resolvePlayerByName(args[0]); - if (targetPlayer == null - || !targetPlayer.isOnline() && !targetPlayer.hasPlayedBefore() - || targetPlayer.getName() == null) - { - GriefPrevention.sendMessage(player, TextMode.Err, Messages.PlayerNotFound2); - return true; - } - - //remember the player's ID for later pet transfer - playerData.petGiveawayRecipient = targetPlayer; - - //send instructions - GriefPrevention.sendMessage(player, TextMode.Instr, Messages.ReadyToTransferPet); - - return true; - } - - //gpblockinfo - else if (cmd.getName().equalsIgnoreCase("gpblockinfo") && player != null) - { - ItemStack inHand = player.getInventory().getItemInMainHand(); - player.sendMessage("In Hand: " + inHand.getType().name()); - - Block inWorld = player.getTargetBlockExact(300, FluidCollisionMode.ALWAYS); - if (inWorld == null) inWorld = player.getEyeLocation().getBlock(); - player.sendMessage("In World: " + inWorld.getType().name()); - - return true; - } - //ignoreplayer else if (cmd.getName().equalsIgnoreCase("ignoreplayer") && player != null) { @@ -2414,17 +2361,8 @@ else if (claim.children.size() > 0 && !deleteTopLevelClaim) else { //delete it - claim.removeSurfaceFluids(null); this.dataStore.deleteClaim(claim, true, false); - //if in a creative mode world, restore the claim area - if (GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner())) - { - GriefPrevention.AddLogEntry(player.getName() + " abandoned a claim @ " + GriefPrevention.getfriendlyLocationString(claim.getLesserBoundaryCorner())); - GriefPrevention.sendMessage(player, TextMode.Warn, Messages.UnclaimCleanupWarning); - GriefPrevention.instance.restoreClaim(claim, 20L * 60 * 2); - } - //adjust claim blocks when abandoning a top level claim if (this.config_claims_abandonReturnRatio != 1.0D && claim.parent == null && claim.ownerID.equals(playerData.playerID)) { @@ -2962,173 +2900,72 @@ public boolean creativeRulesApply(@NotNull Location location) return this.config_claims_worldModes.get(location.getWorld()) == ClaimsMode.Creative; } - public String allowBuild(Player player, Location location) + /** + * @deprecated use {@link ProtectionHelper#checkPermission(Player, Location, ClaimPermission, org.bukkit.event.Event)} + */ + @Deprecated(forRemoval = true, since = "17.0.0") + public @Nullable String allowBuild(Player player, Location location) { - // TODO check all derivatives and rework API return this.allowBuild(player, location, location.getBlock().getType()); } - public String allowBuild(Player player, Location location, Material material) + /** + * @deprecated use {@link ProtectionHelper#checkPermission(Player, Location, ClaimPermission, org.bukkit.event.Event)} + */ + @Deprecated(forRemoval = true, since = "17.0.0") + public @Nullable String allowBuild(Player player, Location location, Material material) { if (!GriefPrevention.instance.claimsEnabledForWorld(location.getWorld())) return null; - PlayerData playerData = this.dataStore.getPlayerData(player.getUniqueId()); - Claim claim = this.dataStore.getClaimAt(location, false, playerData.lastClaim); - - //exception: administrators in ignore claims mode - if (playerData.ignoreClaims) return null; - - //wilderness rules - if (claim == null) + ItemStack placed; + if (material.isItem()) { - //no building in the wilderness in creative mode - if (this.creativeRulesApply(location) || this.config_claims_worldModes.get(location.getWorld()) == ClaimsMode.SurvivalRequiringClaims) + placed = new ItemStack(material); + } + else + { + var blockType = material.asBlockType(); + if (blockType != null && blockType.hasItemType()) { - //exception: when chest claims are enabled, players who have zero land claims and are placing a chest - if (material != Material.CHEST || playerData.getClaims().size() > 0 || GriefPrevention.instance.config_claims_automaticClaimsForNewPlayersRadius == -1) - { - String reason = this.dataStore.getMessage(Messages.NoBuildOutsideClaims); - if (player.hasPermission("griefprevention.ignoreclaims")) - reason += " " + this.dataStore.getMessage(Messages.IgnoreClaimsAdvertisement); - reason += " " + this.dataStore.getMessage(Messages.CreativeBasicsVideo2, DataStore.CREATIVE_VIDEO_URL); - return reason; - } - else - { - return null; - } + placed = blockType.getItemType().createItemStack(); } - - //but it's fine in survival mode else { - return null; + placed = new ItemStack(Material.DIRT); } } - //if not in the wilderness, then apply claim rules (permissions, etc) - else - { - //cache the claim for later reference - playerData.lastClaim = claim; - Block block = location.getBlock(); - - Supplier supplier = claim.checkPermission(player, ClaimPermission.Build, new BlockPlaceEvent(block, block.getState(), block, new ItemStack(material), player, true, EquipmentSlot.HAND)); - - if (supplier == null) return null; - - return supplier.get(); - } + Block block = location.getBlock(); + Supplier result = ProtectionHelper.checkPermission(player, location, ClaimPermission.Build, new BlockPlaceEvent(block, block.getState(), block, placed, player, true, EquipmentSlot.HAND)); + return result == null ? null : result.get(); } - public String allowBreak(Player player, Block block, Location location) + /** + * @deprecated use {@link ProtectionHelper#checkPermission(Player, Location, ClaimPermission, org.bukkit.event.Event)} + */ + @Deprecated(forRemoval = true, since = "17.0.0") + public @Nullable String allowBreak(Player player, Block block, Location location) { return this.allowBreak(player, block, location, new BlockBreakEvent(block, player)); } - public String allowBreak(Player player, Material material, Location location, BlockBreakEvent breakEvent) + /** + * @deprecated use {@link ProtectionHelper#checkPermission(Player, Location, ClaimPermission, org.bukkit.event.Event)} + */ + @Deprecated(forRemoval = true, since = "17.0.0") + public @Nullable String allowBreak(Player player, Material material, Location location, BlockBreakEvent breakEvent) { return this.allowBreak(player, location.getBlock(), location, breakEvent); } - public String allowBreak(Player player, Block block, Location location, BlockBreakEvent breakEvent) + /** + * @deprecated use {@link ProtectionHelper#checkPermission(Player, Location, ClaimPermission, org.bukkit.event.Event)} + */ + @Deprecated(forRemoval = true, since = "17.0.0") + public @Nullable String allowBreak(Player player, Block block, Location location, BlockBreakEvent breakEvent) { - if (!GriefPrevention.instance.claimsEnabledForWorld(location.getWorld())) return null; - - PlayerData playerData = this.dataStore.getPlayerData(player.getUniqueId()); - Claim claim = this.dataStore.getClaimAt(location, false, playerData.lastClaim); - - //exception: administrators in ignore claims mode - if (playerData.ignoreClaims) return null; - - //wilderness rules - if (claim == null) - { - //no building in the wilderness in creative mode - if (this.creativeRulesApply(location) || this.config_claims_worldModes.get(location.getWorld()) == ClaimsMode.SurvivalRequiringClaims) - { - String reason = this.dataStore.getMessage(Messages.NoBuildOutsideClaims); - if (player.hasPermission("griefprevention.ignoreclaims")) - reason += " " + this.dataStore.getMessage(Messages.IgnoreClaimsAdvertisement); - reason += " " + this.dataStore.getMessage(Messages.CreativeBasicsVideo2, DataStore.CREATIVE_VIDEO_URL); - return reason; - } - - //but it's fine in survival mode - else - { - return null; - } - } - else - { - //cache the claim for later reference - playerData.lastClaim = claim; - - //if not in the wilderness, then apply claim rules (permissions, etc) - Supplier cancel = claim.checkPermission(player, ClaimPermission.Build, breakEvent); - if (cancel != null && breakEvent != null) - { - PreventBlockBreakEvent preventionEvent = new PreventBlockBreakEvent(breakEvent); - Bukkit.getPluginManager().callEvent(preventionEvent); - if (preventionEvent.isCancelled()) - { - cancel = null; - } - } - - if (cancel == null) return null; - - return cancel.get(); - } - } - - //restores nature in multiple chunks, as described by a claim instance - //this restores all chunks which have ANY number of claim blocks from this claim in them - //if the claim is still active (in the data store), then the claimed blocks will not be changed (only the area bordering the claim) - public void restoreClaim(Claim claim, long delayInTicks) - { - //admin claims aren't automatically cleaned up when deleted or abandoned - if (claim.isAdminClaim()) return; - - //it's too expensive to do this for huge claims - if (claim.getArea() > 10000 || claim.getWidth() > 250 || claim.getHeight() > 250) return; - - ArrayList chunks = claim.getChunks(); - for (Chunk chunk : chunks) - { - this.restoreChunk(chunk, this.getSeaLevel(chunk.getWorld()) - 15, false, delayInTicks, null); - } - } - - - public void restoreChunk(Chunk chunk, int miny, boolean aggressiveMode, long delayInTicks, Player playerReceivingVisualization) - { - //build a snapshot of this chunk, including 1 block boundary outside of the chunk all the way around - int maxHeight = chunk.getWorld().getMaxHeight(); - BlockSnapshot[][][] snapshots = new BlockSnapshot[18][maxHeight][18]; - Block startBlock = chunk.getBlock(0, 0, 0); - Location startLocation = new Location(chunk.getWorld(), startBlock.getX() - 1, 0, startBlock.getZ() - 1); - for (int x = 0; x < snapshots.length; x++) - { - for (int z = 0; z < snapshots[0][0].length; z++) - { - for (int y = 0; y < snapshots[0].length; y++) - { - Block block = chunk.getWorld().getBlockAt(startLocation.getBlockX() + x, startLocation.getBlockY() + y, startLocation.getBlockZ() + z); - snapshots[x][y][z] = new BlockSnapshot(block.getLocation(), block.getType(), block.getBlockData()); - } - } - } - - //create task to process those data in another thread - Location lesserBoundaryCorner = chunk.getBlock(0, 0, 0).getLocation(); - Location greaterBoundaryCorner = chunk.getBlock(15, 0, 15).getLocation(); - - //create task - //when done processing, this task will create a main thread task to actually update the world with processing results - RestoreNatureProcessingTask task = new RestoreNatureProcessingTask(snapshots, miny, chunk.getWorld().getEnvironment(), lesserBoundaryCorner.getBlock().getBiome(), lesserBoundaryCorner, greaterBoundaryCorner, this.getSeaLevel(chunk.getWorld()), aggressiveMode, GriefPrevention.instance.creativeRulesApply(lesserBoundaryCorner), playerReceivingVisualization); - scheduler.getImpl().runAtLocationLater(lesserBoundaryCorner, task, delayInTicks * 50L, TimeUnit.MILLISECONDS); + Supplier result = ProtectionHelper.checkPermission(player, location, ClaimPermission.Build, breakEvent); + return result == null ? null : result.get(); } private Set parseMaterialListFromConfig(List stringsToParse) @@ -3234,8 +3071,8 @@ static void banPlayer(Player player, String reason, String source) } else { - BanList bans = Bukkit.getServer().getBanList(Type.NAME); - bans.addBan(player.getName(), reason, (Date) null, source); + BanList bans = Bukkit.getServer().getBanList(Type.PROFILE); + bans.addBan(player.getPlayerProfile(), reason, (Date) null, source); //kick if (player.isOnline()) diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/Messages.java b/src/main/java/me/ryanhamshire/GriefPrevention/Messages.java index df9e6cba..af95e7aa 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/Messages.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/Messages.java @@ -18,206 +18,214 @@ package me.ryanhamshire.GriefPrevention; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + public enum Messages { - RespectingClaims, - IgnoringClaims, - SuccessfulAbandon, - RestoreNatureActivate, - RestoreNatureAggressiveActivate, - FillModeActive, - TransferClaimPermission, - TransferClaimMissing, - TransferClaimAdminOnly, - PlayerNotFound2, - TransferTopLevel, - TransferSuccess, - TrustListNoClaim, - ClearPermsOwnerOnly, - UntrustIndividualAllClaims, - UntrustEveryoneAllClaims, - NoPermissionTrust, - ClearPermissionsOneClaim, - UntrustIndividualSingleClaim, - AdminClaimsMode, - BasicClaimsMode, - SubdivisionMode, - SubdivisionVideo2, - DeleteClaimMissing, - DeletionSubdivisionWarning, - DeleteSuccess, - CantDeleteAdminClaim, - DeleteAllSuccess, - NoDeletePermission, - AllAdminDeleted, - AdjustBlocksSuccess, - NotTrappedHere, - RescuePending, - AbandonClaimMissing, - NotYourClaim, - DeleteTopLevelClaim, - AbandonSuccess, - ConfirmAbandonAllClaims, - CantGrantThatPermission, - GrantPermissionNoClaim, - GrantPermissionConfirmation, - ManageUniversalPermissionsInstruction, - ManageOneClaimPermissionsInstruction, - CollectivePublic, - BuildPermission, - ContainersPermission, - AccessPermission, - PermissionsPermission, - LocationCurrentClaim, - LocationAllClaims, - PvPImmunityStart, - DonateItemsInstruction, - ChestFull, - DonationSuccess, - PlayerTooCloseForFire2, - TooDeepToClaim, - ChestClaimConfirmation, - AutomaticClaimNotification, - AutomaticClaimOtherClaimTooClose, - UnprotectedChestWarning, - ThatPlayerPvPImmune, - CantFightWhileImmune, - NoDamageClaimedEntity, - ShovelBasicClaimMode, - RemainingBlocks, - CreativeBasicsVideo2, - SurvivalBasicsVideo2, - TrappedChatKeyword, - TrappedInstructions, - PvPNoDrop, - PvPNoContainers, - PvPImmunityEnd, - NoBedPermission, - NoWildernessBuckets, - NoLavaNearOtherPlayer, - TooFarAway, - BlockNotClaimed, - BlockClaimed, - RestoreNaturePlayerInChunk, - NoCreateClaimPermission, - ResizeNeedMoreBlocks, - NoCreativeUnClaim, - ClaimResizeSuccess, - ResizeFailOverlap, - ResizeStart, - ResizeFailOverlapSubdivision, - SubdivisionStart, - CreateSubdivisionOverlap, - SubdivisionSuccess, - CreateClaimFailOverlap, - CreateClaimFailOverlapOtherPlayer, - ClaimsDisabledWorld, - ClaimStart, - NewClaimTooNarrow, - CreateClaimInsufficientBlocks, - AbandonClaimAdvertisement, - CreateClaimFailOverlapShort, - CreateClaimSuccess, - RescueAbortedMoved, - OnlyOwnersModifyClaims, - NoBuildPvP, - NoBuildPermission, - NoAccessPermission, - NoContainersPermission, - OwnerNameForAdminClaims, - UnknownPlayerName, - ClaimTooSmallForEntities, - TooManyEntitiesInClaim, - YouHaveNoClaims, - ConfirmFluidRemoval, - AutoBanNotify, - AdjustGroupBlocksSuccess, - InvalidPermissionID, - HowToClaimRegex, - NoBuildOutsideClaims, - PlayerOfflineTime, - BuildingOutsideClaims, - TrappedWontWorkHere, - CommandBannedInPvP, - UnclaimCleanupWarning, - NoTeleportPvPCombat, - NoTNTDamageAboveSeaLevel, - NoTNTDamageClaims, - IgnoreClaimsAdvertisement, - NoPermissionForCommand, - ClaimsListNoPermission, - ExplosivesDisabled, - ExplosivesEnabled, - ClaimExplosivesAdvertisement, - PlayerInPvPSafeZone, - NoPistonsOutsideClaims, - SoftMuted, - UnSoftMuted, - DropUnlockAdvertisement, - PickupBlockedExplanation, - DropUnlockConfirmation, - DropUnlockOthersConfirmation, - AdvertiseACandACB, - AdvertiseAdminClaims, - AdvertiseACB, - NotYourPet, - PetGiveawayConfirmation, - PetTransferCancellation, - ReadyToTransferPet, - AvoidGriefClaimLand, - BecomeMayor, - ClaimCreationFailedOverClaimCountLimit, - CreateClaimFailOverlapRegion, - ResizeFailOverlapRegion, - ShowNearbyClaims, - NoChatUntilMove, - SetClaimBlocksSuccess, - IgnoreConfirmation, - NotIgnoringPlayer, - UnIgnoreConfirmation, - SeparateConfirmation, - UnSeparateConfirmation, - NotIgnoringAnyone, - TrustListHeader, - Manage, - Build, - Containers, - Access, - HasSubclaimRestriction, - StartBlockMath, - ClaimsListHeader, - ContinueBlockMath, - EndBlockMath, - NoClaimDuringPvP, - UntrustAllOwnerOnly, - ManagersDontUntrustManagers, - BookAuthor, - BookTitle, - BookIntro, - BookDisabledChestClaims, - BookUsefulCommands, - BookLink, - BookTools, - ResizeClaimTooNarrow, - ResizeClaimInsufficientArea, - NoProfanity, - PlayerNotIgnorable, - NoEnoughBlocksForChestClaim, - IsIgnoringYou, - MustHoldModificationToolForThat, - StandInClaimToResize, - ClaimsExtendToSky, - ClaimsAutoExtendDownward, - MinimumRadius, - RadiusRequiresGoldenShovel, - ClaimTooSmallForActiveBlocks, - TooManyActiveBlocksInClaim, - ConsoleOnlyCommand, - WorldNotFound, - AdjustBlocksAllSuccess, - TooMuchIpOverlap, - StandInSubclaim, - SubclaimRestricted, - SubclaimUnrestricted, - NetherPortalTrapDetectionMessage + RespectingClaims("Now respecting claims."), + IgnoringClaims("Now ignoring claims."), + NoCreativeUnClaim("You can't unclaim this land. You can only make this claim larger or create additional claims."), + SuccessfulAbandon("Claims abandoned. You now have {0} available claim blocks.", "0: remaining blocks"), + TransferClaimPermission("That command requires the administrative claims permission."), + TransferClaimMissing("There's no claim here. Stand in the administrative claim you want to transfer."), + TransferClaimAdminOnly("Only administrative claims may be transferred to a player."), + PlayerNotFound2("No player by that name has logged in recently."), + TransferTopLevel("Only top level claims (not subdivisions) may be transferred. Stand outside of the subdivision and try again."), + TransferSuccess("Claim transferred."), + TrustListNoClaim("Stand inside the claim you're curious about."), + ClearPermsOwnerOnly("Only the claim owner can clear all permissions."), + UntrustIndividualAllClaims("Revoked {0}'s access to ALL your claims. To set permissions for a single claim, stand inside it.", "0: untrusted player"), + UntrustEveryoneAllClaims("Cleared permissions in ALL your claims. To set permissions for a single claim, stand inside it."), + NoPermissionTrust("You don't have {0}'s permission to manage permissions here.", "0: claim owner's name"), + ClearPermissionsOneClaim("Cleared permissions in this claim. To set permission for ALL your claims, stand outside them."), + UntrustIndividualSingleClaim("Revoked {0}'s access to this claim. To set permissions for a ALL your claims, stand outside them.", "0: untrusted player"), + AdminClaimsMode("Administrative claims mode active. Any claims created will be free and editable by other administrators."), + BasicClaimsMode("Returned to basic claim creation mode."), + SubdivisionMode("Subdivision mode. Use your shovel to create subdivisions in your existing claims. Use /basicclaims to exit."), + SubdivisionVideo2("Click for Subdivision Help: {0}", "0:video URL"), + DeleteClaimMissing("There's no claim here."), + DeletionSubdivisionWarning("This claim includes subdivisions. If you're sure you want to delete it, use /deleteclaim again."), + DeleteSuccess("Claim deleted."), + CantDeleteAdminClaim("You don't have permission to delete administrative claims."), + DeleteAllSuccess("Deleted all of {0}'s claims.", "0: owner's name"), + NoDeletePermission("You don't have permission to delete claims."), + AllAdminDeleted("Deleted all administrative claims."), + AdjustBlocksSuccess("Adjusted {0}'s bonus claim blocks by {1}. New total bonus blocks: {2}.", "0: player; 1: adjustment; 2: new total"), + AdjustBlocksAllSuccess("Adjusted all online players' bonus claim blocks by {0}.", "0: adjustment amount"), + NotTrappedHere("You can build here. Save yourself."), + RescuePending("If you stay put for 10 seconds, you'll be teleported out. Please wait."), + AbandonClaimMissing("Stand in the claim you want to delete, or consider /abandonallclaims."), + NotYourClaim("This isn't your claim."), + DeleteTopLevelClaim("To delete a subdivision, stand inside it. Otherwise, use /abandontoplevelclaim to delete this claim and all subdivisions."), + AbandonSuccess("Claim abandoned. You now have {0} available claim blocks.", "0: remaining claim blocks"), + ConfirmAbandonAllClaims("Are you sure you want to abandon ALL of your claims? Please confirm with /abandonallclaims confirm"), + CantGrantThatPermission("You can't grant a permission you don't have yourself."), + GrantPermissionNoClaim("Stand inside the claim where you want to grant permission."), + GrantPermissionConfirmation("Granted {0} permission to {1} {2}.", "0: target player; 1: permission description; 2: scope (changed claims)"), + ManageUniversalPermissionsInstruction("To manage permissions for ALL your claims, stand outside them."), + ManageOneClaimPermissionsInstruction("To manage permissions for a specific claim, stand inside it."), + CollectivePublic("the public", "as in 'granted the public permission to...'"), + BuildPermission("build"), + ContainersPermission("access containers and animals"), + AccessPermission("use buttons and levers"), + PermissionsPermission("manage permissions"), + LocationCurrentClaim("in this claim"), + LocationAllClaims("in all your claims"), + PvPImmunityStart("You're protected from attack by other players as long as your inventory is empty."), + DonateItemsInstruction("To give away the item(s) in your hand, left-click the chest again."), + ChestFull("This chest is full."), + DonationSuccess("Item(s) transferred to chest!"), + PlayerTooCloseForFire2("You can't start a fire this close to another player."), + TooDeepToClaim("This chest can't be protected because it's too deep underground. Consider moving it."), + ChestClaimConfirmation("This chest is protected."), + AutomaticClaimNotification("This chest and nearby blocks are protected from breakage and theft."), + AutomaticClaimOtherClaimTooClose("Cannot create a claim for your chest, there is another claim too close!"), + UnprotectedChestWarning("This chest is NOT protected. Consider using a golden shovel to expand an existing claim or to create a new one."), + ThatPlayerPvPImmune("You can't injure defenseless players."), + CantFightWhileImmune("You can't fight someone while you're protected from PvP."), + NoDamageClaimedEntity("That belongs to {0}.", "0: owner name"), + ShovelBasicClaimMode("Shovel returned to basic claims mode."), + RemainingBlocks("You may claim up to {0} more blocks.", "0: remaining blocks"), + CreativeBasicsVideo2("Click for Land Claim Help: {0}", "{0}: video URL"), + SurvivalBasicsVideo2("Click for Land Claim Help: {0}", "{0}: video URL"), + TrappedChatKeyword("trapped;stuck", "When mentioned in chat, players get information about the /trapped command (multiple words can be separated with semi-colons)"), + TrappedInstructions("Are you trapped in someone's land claim? Try the /trapped command."), + PvPNoDrop("You can't drop items while in PvP combat."), + PvPNoContainers("You can't access containers during PvP combat."), + PvPImmunityEnd("Now you can fight with other players."), + NoBedPermission("{0} hasn't given you permission to sleep here.", "0: claim owner"), + NoWildernessBuckets("You may only dump buckets inside your claim(s) or underground."), + NoLavaNearOtherPlayer("You can't place lava this close to {0}.", "0: nearby player"), + TooFarAway("That's too far away."), + BlockNotClaimed("No one has claimed this block."), + BlockClaimed("That block has been claimed by {0}.", "0: claim owner"), + NoCreateClaimPermission("You don't have permission to claim land."), + ResizeClaimTooNarrow("This new size would be too small. Claims must be at least {0} blocks wide.", "0: minimum claim width"), + ResizeNeedMoreBlocks("You don't have enough blocks for this size. You need {0} more.", "0: how many needed"), + ClaimResizeSuccess("Claim resized. {0} available claim blocks remaining.", "0: remaining blocks"), + ResizeFailOverlap("Can't resize here because it would overlap another nearby claim."), + ResizeStart("Resizing claim. Use your shovel again at the new location for this corner."), + ResizeFailOverlapSubdivision("You can't create a subdivision here because it would overlap another subdivision. Consider /abandonclaim to delete it, or use your shovel at a corner to resize it."), + SubdivisionStart("Subdivision corner set! Use your shovel at the location for the opposite corner of this new subdivision."), + CreateSubdivisionOverlap("Your selected area overlaps another subdivision."), + SubdivisionSuccess("Subdivision created! Use /trust to share it with friends."), + CreateClaimFailOverlap("You can't create a claim here because it would overlap your other claim. Use /abandonclaim to delete it, or use your shovel at a corner to resize it."), + CreateClaimFailOverlapOtherPlayer("You can't create a claim here because it would overlap {0}'s claim.", "0: other claim owner"), + ClaimsDisabledWorld("Land claims are disabled in this world."), + ClaimStart("Claim corner set! Use the shovel again at the opposite corner to claim a rectangle of land. To cancel, put your shovel away."), + NewClaimTooNarrow("This claim would be too small. Any claim must be at least {0} blocks wide.", "0: minimum claim width"), + ResizeClaimInsufficientArea("This claim would be too small. Any claim must use at least {0} total claim blocks.", "0: minimum claim area"), + CreateClaimInsufficientBlocks("You don't have enough blocks to claim that entire area. You need {0} more blocks.", "0: additional blocks needed"), + AbandonClaimAdvertisement("To delete another claim and free up some blocks, use /abandonclaim."), + CreateClaimFailOverlapShort("Your selected area overlaps an existing claim."), + CreateClaimSuccess("Claim created! Use /trust to share it with friends."), + RescueAbortedMoved("You moved! Rescue cancelled."), + OnlyOwnersModifyClaims("Only {0} can modify this claim.", "0: owner name"), + NoBuildPvP("You can't build in claims during PvP combat."), + NoBuildPermission("You don't have {0}'s permission to build here.", "0: owner name"), + NoAccessPermission("You don't have {0}'s permission to use that.", "0: owner name. access permission controls buttons, levers, and beds"), + NoContainersPermission("You don't have {0}'s permission to use that.", "0: owner's name. containers also include crafting blocks"), + OwnerNameForAdminClaims("an administrator", "as in 'You don't have an administrator's permission to build here.'"), + UnknownPlayerName("someone", "Name used for unknown players. UUID will be appended if available: \"someone (01234567-0123-0123-0123-0123456789ab)\""), + ClaimTooSmallForEntities("This claim isn't big enough for that. Try enlarging it."), + TooManyEntitiesInClaim("This claim has too many entities already. Try enlarging the claim or removing some animals, monsters, paintings, or minecarts."), + YouHaveNoClaims("You don't have any land claims."), + AutoBanNotify("Auto-banned {0}({1}). See logs for details."), + AdjustGroupBlocksSuccess("Adjusted bonus claim blocks for players with the {0} permission by {1}. New total: {2}.", "0: permission; 1: adjustment amount; 2: new total bonus"), + InvalidPermissionID("Please specify a player name, or a permission in [brackets]."), + HowToClaimRegex("(^|.*\\W)how\\W.*\\W(claim|protect|lock)(\\W.*|$)", "This is a Java Regular Expression. Look it up before editing! It's used to tell players about the demo video when they ask how to claim land."), + NoBuildOutsideClaims("You can't build here unless you claim some land first."), + PlayerOfflineTime(" Last login: {0} days ago.", "0: number of full days since last login"), + BuildingOutsideClaims("Other players can build here, too. Consider creating a land claim to protect your work!"), + TrappedWontWorkHere("Sorry, unable to find a safe location to teleport you to. Contact an admin."), + CommandBannedInPvP("You can't use that command while in PvP combat."), + NoTeleportPvPCombat("You can't teleport while fighting another player."), + NoTNTDamageAboveSeaLevel("Warning: TNT will not destroy blocks above sea level."), + NoTNTDamageClaims("Warning: TNT will not destroy claimed blocks."), + IgnoreClaimsAdvertisement("To override, use /ignoreclaims."), + NoPermissionForCommand("You don't have permission to do that."), + ClaimsListNoPermission("You don't have permission to get information about another player's land claims."), + ExplosivesDisabled("This claim is now protected from explosions. Use /claimexplosions again to disable."), + ExplosivesEnabled("This claim is now vulnerable to explosions. Use /claimexplosions again to re-enable protections."), + ClaimExplosivesAdvertisement("To allow explosives to destroy blocks in this land claim, use /claimexplosions."), + PlayerInPvPSafeZone("That player is in a PvP safe zone."), + NoPistonsOutsideClaims("Warning: Pistons won't move blocks outside land claims."), + SoftMuted("Soft-muted {0}.", "0: The changed player's name."), + UnSoftMuted("Un-soft-muted {0}.", "0: The changed player's name."), + DropUnlockAdvertisement("Other players can't pick up your dropped items unless you /unlockdrops first."), + PickupBlockedExplanation("You can't pick this up unless {0} uses /unlockdrops.", "0: The item stack's owner."), + DropUnlockConfirmation("Unlocked your drops. Other players may now pick them up (until you die again)."), + DropUnlockOthersConfirmation("Unlocked {0}'s drops.", "0: The owner of the unlocked drops."), + AdvertiseACandACB("You may use /acb to give yourself more claim blocks, or /adminclaims to create a free administrative claim."), + AdvertiseAdminClaims("You could create an administrative land claim instead using /adminclaims, which you'd share with other administrators."), + AdvertiseACB("You may use /acb to give yourself more claim blocks."), + NotYourPet("That belongs to {0}.", "0: owner name"), + AvoidGriefClaimLand("Prevent grief! If you claim your land, you will be grief-proof."), + BecomeMayor("Subdivide your land claim and become a mayor!"), + ClaimCreationFailedOverClaimCountLimit("You've reached your limit on land claims. Use /abandonclaim to remove one before creating another."), + CreateClaimFailOverlapRegion("You can't claim all of this because you're not allowed to build here."), + ResizeFailOverlapRegion("You don't have permission to build there, so you can't claim that area."), + ShowNearbyClaims("Found {0} land claims.", "0: Number of claims found."), + NoChatUntilMove("Sorry, but you have to move a little more before you can chat. We get lots of spam bots here. :)"), + SetClaimBlocksSuccess("Updated accrued claim blocks."), + IgnoreConfirmation("You're now ignoring chat messages from that player."), + UnIgnoreConfirmation("You're no longer ignoring chat messages from that player."), + NotIgnoringPlayer("You're not ignoring that player."), + SeparateConfirmation("Those players will now ignore each other in chat."), + UnSeparateConfirmation("Those players will no longer ignore each other in chat."), + NotIgnoringAnyone("You're not ignoring anyone."), + TrustListHeader("Explicit permissions here:", "0: The claim's owner"), + Manage("Manage"), + Build("Build"), + Containers("Containers"), + Access("Access"), + HasSubclaimRestriction("This subclaim does not inherit permissions from the parent"), + StartBlockMath("{0} blocks from play + {1} bonus = {2} total."), + ClaimsListHeader("Claims:"), + ContinueBlockMath(" (-{0} blocks)"), + EndBlockMath(" = {0} blocks left to spend"), + NoClaimDuringPvP("You can't claim lands during PvP combat."), + UntrustAllOwnerOnly("Only the claim owner can clear all its permissions."), + ManagersDontUntrustManagers("Only the claim owner can demote a manager."), + PlayerNotIgnorable("You can't ignore that player."), + NoEnoughBlocksForChestClaim("Because you don't have any claim blocks available, no automatic land claim was created for you. You can use /claimslist to monitor your available claim block total."), + MustHoldModificationToolForThat("You must be holding a golden shovel to do that."), + StandInClaimToResize("Stand inside the land claim you want to resize."), + ClaimsExtendToSky("Land claims always extend to max build height."), + ClaimsAutoExtendDownward("Land claims auto-extend deeper into the ground when you place blocks under them."), + MinimumRadius("Minimum radius is {0}.", "0: minimum radius"), + RadiusRequiresGoldenShovel("You must be holding a golden shovel when specifying a radius."), + ClaimTooSmallForActiveBlocks("This claim isn't big enough to support any active block types (hoppers, spawners, beacons...). Make the claim bigger first."), + TooManyActiveBlocksInClaim("This claim is at its limit for active block types (hoppers, spawners, beacons...). Either make it bigger, or remove other active blocks first."), + BookAuthor("BigScary"), + BookTitle("How to Claim Land"), + BookLink("Click: {0}", "{0}: video URL"), + BookIntro("Claim land to protect your stuff! Click the link above to learn land claims in 3 minutes or less. :)"), + BookTools("Our claim tools are {0} and {1}.", "0: claim modification tool name; 1:claim information tool name"), + BookDisabledChestClaims(" On this server, placing a chest will NOT claim land for you."), + BookUsefulCommands("Useful Commands:"), + NoProfanity("Please moderate your language."), + IsIgnoringYou("That player is ignoring you."), + ConsoleOnlyCommand("That command may only be executed from the server console."), + WorldNotFound("World not found."), + TooMuchIpOverlap("Sorry, there are too many players logged in with your IP address."), + StandInSubclaim("You need to be standing in a subclaim to restrict it"), + SubclaimRestricted("This subclaim's permissions will no longer inherit from the parent claim"), + SubclaimUnrestricted("This subclaim's permissions will now inherit from the parent claim"), + NetherPortalTrapDetectionMessage("It seems you might be stuck inside a nether portal. We will rescue you in a few seconds if that is the case!", "Sent to player on join, if they left while inside a nether portal."); + + final @NotNull String defaultValue; + final @Nullable String notes; + + Messages(@NotNull String defaultValue, @NotNull String notes) { + this.defaultValue = defaultValue; + this.notes = notes; + } + + Messages(@NotNull String defaultValue) { + this.defaultValue = defaultValue; + this.notes = null; + } + } diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/PlayerData.java b/src/main/java/me/ryanhamshire/GriefPrevention/PlayerData.java index 56313032..6072f7d0 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/PlayerData.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/PlayerData.java @@ -54,9 +54,6 @@ public class PlayerData //what "mode" the shovel is in determines what it will do when it's used public ShovelMode shovelMode = ShovelMode.Basic; - //radius for restore nature fill mode - int fillRadius = 0; - //last place the player used the shovel, useful in creating and resizing claims, //because the player must use the shovel twice in those instances public Location lastShovelLocation = null; @@ -114,9 +111,6 @@ public class PlayerData //message to send to player after he respawns String messageOnRespawn = null; - //player which a pet will be given to when it's right-clicked - OfflinePlayer petGiveawayRecipient = null; - //timestamp for last "you're building outside your land claims" message Long buildWarningTimestamp = null; diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java b/src/main/java/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java index c4479eab..b1cc97b1 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java @@ -18,6 +18,7 @@ package me.ryanhamshire.GriefPrevention; +import com.griefprevention.protection.ProtectionHelper; import com.griefprevention.util.command.MonitorableCommand; import com.griefprevention.util.command.MonitoredCommands; import com.griefprevention.visualization.BoundaryVisualization; @@ -28,7 +29,6 @@ import org.bukkit.BanList; import org.bukkit.Bukkit; import org.bukkit.ChatColor; -import org.bukkit.Chunk; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; @@ -39,7 +39,6 @@ import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Levelled; import org.bukkit.block.data.Waterlogged; import org.bukkit.entity.AbstractHorse; import org.bukkit.entity.Animals; @@ -89,6 +88,7 @@ import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; +import org.bukkit.profile.PlayerProfile; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.util.BlockIterator; @@ -690,7 +690,8 @@ else if (address.equals(playerData.ipAddress.toString())) if (info2.address.toString().equals(address)) { OfflinePlayer bannedAccount = instance.getServer().getOfflinePlayer(info2.bannedAccountName); - instance.getServer().getBanList(BanList.Type.NAME).pardon(bannedAccount.getName()); + BanList banList = instance.getServer().getBanList(BanList.Type.PROFILE); + banList.pardon(bannedAccount.getPlayerProfile()); this.tempBannedIps.remove(j--); } } @@ -1082,9 +1083,8 @@ public void onPlayerInteractEntity(PlayerInteractEntityEvent event) PlayerData playerData = this.dataStore.getPlayerData(player.getUniqueId()); //if entity is tameable and has an owner, apply special rules - if (entity instanceof Tameable) + if (entity instanceof Tameable tameable) { - Tameable tameable = (Tameable) entity; if (tameable.isTamed()) { if (tameable.getOwner() != null) @@ -1094,15 +1094,6 @@ public void onPlayerInteractEntity(PlayerInteractEntityEvent event) //if the player interacting is the owner or an admin in ignore claims mode, always allow if (player.getUniqueId().equals(ownerID) || playerData.ignoreClaims) { - //if giving away pet, do that instead - if (playerData.petGiveawayRecipient != null) - { - tameable.setOwner(playerData.petGiveawayRecipient); - playerData.petGiveawayRecipient = null; - GriefPrevention.sendMessage(player, TextMode.Success, Messages.PetGiveawayConfirmation); - event.setCancelled(true); - } - return; } if (!instance.pvpRulesApply(entity.getLocation().getWorld()) || instance.config_pvp_protectPets) @@ -1135,10 +1126,10 @@ public void onPlayerInteractEntity(PlayerInteractEntityEvent event) //don't allow interaction with item frames or armor stands in claimed areas without build permission if (entity.getType() == EntityType.ARMOR_STAND || entity instanceof Hanging) { - String noBuildReason = instance.allowBuild(player, entity.getLocation(), Material.ITEM_FRAME); + Supplier noBuildReason = ProtectionHelper.checkPermission(player, entity.getLocation(), ClaimPermission.Build, event); if (noBuildReason != null) { - GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason); + GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason.get()); event.setCancelled(true); return; } @@ -1366,10 +1357,10 @@ public void onPlayerBucketEmpty(PlayerBucketEmptyEvent bucketEvent) } //make sure the player is allowed to build at the location - String noBuildReason = instance.allowBuild(player, block.getLocation(), Material.WATER); + Supplier noBuildReason = ProtectionHelper.checkPermission(player, block.getLocation(), ClaimPermission.Build, bucketEvent); if (noBuildReason != null) { - GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason); + GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason.get()); bucketEvent.setCancelled(true); return; } @@ -1465,22 +1456,22 @@ public void onPlayerBucketFill(PlayerBucketFillEvent bucketEvent) if (!instance.claimsEnabledForWorld(block.getWorld())) return; - //make sure the player is allowed to build at the location - String noBuildReason = instance.allowBuild(player, block.getLocation(), Material.AIR); - if (noBuildReason != null) + //exemption for cow milking (permissions will be handled by player interact with entity event instead) + Material blockType = block.getType(); + if (blockType == Material.AIR) + return; + if (blockType.isSolid()) { - //exemption for cow milking (permissions will be handled by player interact with entity event instead) - Material blockType = block.getType(); - if (blockType == Material.AIR) + BlockData blockData = block.getBlockData(); + if (!(blockData instanceof Waterlogged) || !((Waterlogged) blockData).isWaterlogged()) return; - if (blockType.isSolid()) - { - BlockData blockData = block.getBlockData(); - if (!(blockData instanceof Waterlogged) || !((Waterlogged) blockData).isWaterlogged()) - return; - } + } - GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason); + //make sure the player is allowed to build at the location + Supplier noBuildReason = ProtectionHelper.checkPermission(player, block.getLocation(), ClaimPermission.Build, bucketEvent); + if (noBuildReason != null) + { + GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason.get()); bucketEvent.setCancelled(true); return; } @@ -1497,14 +1488,14 @@ void onPlayerSignOpen(@NotNull PlayerSignOpenEvent event) } Player player = event.getPlayer(); - String denial = instance.allowBuild(player, event.getSign().getLocation(), event.getSign().getType()); + Supplier denial = ProtectionHelper.checkPermission(player, event.getSign().getLocation(), ClaimPermission.Build, event); // If user is allowed to build, do nothing. if (denial == null) return; // If user is not allowed to build, prevent sign UI opening and send message. - GriefPrevention.sendMessage(player, TextMode.Err, denial); + GriefPrevention.sendMessage(player, TextMode.Err, denial.get()); event.setCancelled(true); } @@ -1737,13 +1728,10 @@ else if (clickedBlock != null && || materialInHand == Material.HONEYCOMB || dyes.contains(materialInHand))) { - String noBuildReason = instance - .allowBuild(player, clickedBlock - .getLocation(), - clickedBlockType); + Supplier noBuildReason = ProtectionHelper.checkPermission(player, event.getClickedBlock().getLocation(), ClaimPermission.Build, event); if (noBuildReason != null) { - GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason); + GriefPrevention.sendMessage(player, TextMode.Err, noBuildReason.get()); event.setCancelled(true); } @@ -1927,168 +1915,6 @@ else if (materialInHand == instance.config_claims_investigationTool && hand == E return; } - //if the player is in restore nature mode, do only that - UUID playerID = player.getUniqueId(); - playerData = this.dataStore.getPlayerData(player.getUniqueId()); - if (playerData.shovelMode == ShovelMode.RestoreNature || playerData.shovelMode == ShovelMode.RestoreNatureAggressive) - { - //if the clicked block is in a claim, visualize that claim and deliver an error message - Claim claim = this.dataStore.getClaimAt(clickedBlock.getLocation(), false, playerData.lastClaim); - if (claim != null) - { - GriefPrevention.sendMessage(player, TextMode.Err, Messages.BlockClaimed, claim.getOwnerName()); - BoundaryVisualization.visualizeClaim(player, claim, VisualizationType.CONFLICT_ZONE, clickedBlock); - return; - } - - //figure out which chunk to repair - Chunk chunk = player.getWorld().getChunkAt(clickedBlock.getLocation()); - //start the repair process - - //set boundaries for processing - int miny = clickedBlock.getY(); - - //if not in aggressive mode, extend the selection down to a little below sea level - if (!(playerData.shovelMode == ShovelMode.RestoreNatureAggressive)) - { - if (miny > instance.getSeaLevel(chunk.getWorld()) - 10) - { - miny = instance.getSeaLevel(chunk.getWorld()) - 10; - } - } - - instance.restoreChunk(chunk, miny, playerData.shovelMode == ShovelMode.RestoreNatureAggressive, 0, player); - - return; - } - - //if in restore nature fill mode - if (playerData.shovelMode == ShovelMode.RestoreNatureFill) - { - ArrayList allowedFillBlocks = new ArrayList<>(); - Environment environment = clickedBlock.getWorld().getEnvironment(); - if (environment == Environment.NETHER) - { - allowedFillBlocks.add(Material.NETHERRACK); - } - else if (environment == Environment.THE_END) - { - allowedFillBlocks.add(Material.END_STONE); - } - else - { - allowedFillBlocks.add(Material.SHORT_GRASS); - allowedFillBlocks.add(Material.DIRT); - allowedFillBlocks.add(Material.STONE); - allowedFillBlocks.add(Material.SAND); - allowedFillBlocks.add(Material.SANDSTONE); - allowedFillBlocks.add(Material.ICE); - } - - Block centerBlock = clickedBlock; - - int maxHeight = centerBlock.getY(); - int minx = centerBlock.getX() - playerData.fillRadius; - int maxx = centerBlock.getX() + playerData.fillRadius; - int minz = centerBlock.getZ() - playerData.fillRadius; - int maxz = centerBlock.getZ() + playerData.fillRadius; - int minHeight = maxHeight - 10; - minHeight = Math.max(minHeight, clickedBlock.getWorld().getMinHeight()); - - Claim cachedClaim = null; - for (int x = minx; x <= maxx; x++) - { - for (int z = minz; z <= maxz; z++) - { - //circular brush - Location location = new Location(centerBlock.getWorld(), x, centerBlock.getY(), z); - if (location.distance(centerBlock.getLocation()) > playerData.fillRadius) continue; - - //default fill block is initially the first from the allowed fill blocks list above - Material defaultFiller = allowedFillBlocks.get(0); - - //prefer to use the block the player clicked on, if it's an acceptable fill block - if (allowedFillBlocks.contains(centerBlock.getType())) - { - defaultFiller = centerBlock.getType(); - } - - //if the player clicks on water, try to sink through the water to find something underneath that's useful for a filler - else if (centerBlock.getType() == Material.WATER) - { - Block block = centerBlock.getWorld().getBlockAt(centerBlock.getLocation()); - while (!allowedFillBlocks.contains(block.getType()) && block.getY() > centerBlock.getY() - 10) - { - block = block.getRelative(BlockFace.DOWN); - } - if (allowedFillBlocks.contains(block.getType())) - { - defaultFiller = block.getType(); - } - } - - //fill bottom to top - for (int y = minHeight; y <= maxHeight; y++) - { - Block block = centerBlock.getWorld().getBlockAt(x, y, z); - - //respect claims - Claim claim = this.dataStore.getClaimAt(block.getLocation(), false, cachedClaim); - if (claim != null) - { - cachedClaim = claim; - break; - } - - //only replace air, spilling water, snow, long grass - if (block.getType() == Material.AIR || block.getType() == Material.SNOW || (block.getType() == Material.WATER && ((Levelled) block.getBlockData()).getLevel() != 0) || block.getType() == Material.SHORT_GRASS) - { - //if the top level, always use the default filler picked above - if (y == maxHeight) - { - block.setType(defaultFiller); - } - - //otherwise look to neighbors for an appropriate fill block - else - { - Block eastBlock = block.getRelative(BlockFace.EAST); - Block westBlock = block.getRelative(BlockFace.WEST); - Block northBlock = block.getRelative(BlockFace.NORTH); - Block southBlock = block.getRelative(BlockFace.SOUTH); - - //first, check lateral neighbors (ideally, want to keep natural layers) - if (allowedFillBlocks.contains(eastBlock.getType())) - { - block.setType(eastBlock.getType()); - } - else if (allowedFillBlocks.contains(westBlock.getType())) - { - block.setType(westBlock.getType()); - } - else if (allowedFillBlocks.contains(northBlock.getType())) - { - block.setType(northBlock.getType()); - } - else if (allowedFillBlocks.contains(southBlock.getType())) - { - block.setType(southBlock.getType()); - } - - //if all else fails, use the default filler selected above - else - { - block.setType(defaultFiller); - } - } - } - } - } - } - - return; - } - //if the player doesn't have claims permission, don't do anything if (!player.hasPermission("griefprevention.createclaims")) { @@ -2096,6 +1922,8 @@ else if (allowedFillBlocks.contains(southBlock.getType())) return; } + playerData = this.dataStore.getPlayerData(player.getUniqueId()); + //if he's resizing a claim and that claim hasn't been deleted since he started resizing it if (playerData.claimResizing != null && playerData.claimResizing.inDataStore) { @@ -2315,6 +2143,8 @@ else if (playerData.shovelMode == ShovelMode.Subdivide) } } + UUID playerID = player.getUniqueId(); + //if not an administrative claim, verify the player has enough claim blocks for this new claim if (playerData.shovelMode != ShovelMode.Admin) { @@ -2445,10 +2275,7 @@ static Block getTargetBlock(Player player, int maxDistance) throws IllegalStateE { result = iterator.next(); Material type = result.getType(); - if (type != Material.AIR && - (!passThroughWater || type != Material.WATER) && - type != Material.SHORT_GRASS && - type != Material.SNOW) return result; + if (!Tag.REPLACEABLE.isTagged(type) || (!passThroughWater && type == Material.WATER)) return result; } return result; diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/RestoreNatureExecutionTask.java b/src/main/java/me/ryanhamshire/GriefPrevention/RestoreNatureExecutionTask.java deleted file mode 100644 index f0c5c629..00000000 --- a/src/main/java/me/ryanhamshire/GriefPrevention/RestoreNatureExecutionTask.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - GriefPrevention Server Plugin for Minecraft - Copyright (C) 2012 Ryan Hamshire - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - */ - -package me.ryanhamshire.GriefPrevention; - -import com.griefprevention.visualization.BoundaryVisualization; -import com.griefprevention.visualization.VisualizationType; -import me.ryanhamshire.GriefPrevention.util.BoundingBox; -import org.bukkit.Chunk; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.entity.Animals; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Hanging; -import org.bukkit.entity.Player; - -//this main thread task takes the output from the RestoreNatureProcessingTask\ -//and updates the world accordingly -class RestoreNatureExecutionTask implements Runnable -{ - //results from processing thread - //will be applied to the world - private final BlockSnapshot[][][] snapshots; - - //boundaries for changes - private final int miny; - private final Location lesserCorner; - private final Location greaterCorner; - - //player who should be notified about the result (will see a visualization when the restoration is complete) - private final Player player; - - public RestoreNatureExecutionTask(BlockSnapshot[][][] snapshots, int miny, Location lesserCorner, Location greaterCorner, Player player) - { - this.snapshots = snapshots; - this.miny = miny; - this.lesserCorner = lesserCorner; - this.greaterCorner = greaterCorner; - this.player = player; - } - - - @Override - public void run() - { - //apply changes to the world, but ONLY to unclaimed blocks - //note that the edge of the results is not applied (the 1-block-wide band around the outside of the chunk) - //those data were sent to the processing thread for referernce purposes, but aren't part of the area selected for restoration - Claim cachedClaim = null; - for (int x = 1; x < this.snapshots.length - 1; x++) - { - for (int z = 1; z < this.snapshots[0][0].length - 1; z++) - { - for (int y = this.miny; y < this.snapshots[0].length; y++) - { - BlockSnapshot blockUpdate = this.snapshots[x][y][z]; - Block currentBlock = blockUpdate.location.getBlock(); - if (blockUpdate.typeId != currentBlock.getType() || !blockUpdate.data.equals(currentBlock.getBlockData())) - { - Claim claim = GriefPrevention.instance.dataStore.getClaimAt(blockUpdate.location, false, cachedClaim); - if (claim != null) - { - cachedClaim = claim; - break; - } - - try - { - currentBlock.setType(blockUpdate.typeId, false); - // currentBlock.setBlockData(blockUpdate.data, false); - } - catch (IllegalArgumentException e) - { - //just don't update this block and continue trying to update other blocks - } - } - } - } - } - - //clean up any entities in the chunk, ensure no players are suffocated - Chunk chunk = this.lesserCorner.getChunk(); - Entity[] entities = chunk.getEntities(); - for (Entity entity : entities) - { - if (!(entity instanceof Player || entity instanceof Animals)) - { - //hanging entities (paintings, item frames) are protected when they're in land claims - if (!(entity instanceof Hanging) || GriefPrevention.instance.dataStore.getClaimAt(entity.getLocation(), false, null) == null) - { - //everything else is removed - entity.remove(); - } - } - - //for players, always ensure there's air where the player is standing - else - { - Block feetBlock = entity.getLocation().getBlock(); - feetBlock.setType(Material.AIR); - feetBlock.getRelative(BlockFace.UP).setType(Material.AIR); - } - } - - //show visualization to player who started the restoration - if (player != null) - { - BoundaryVisualization.visualizeArea( - player, - new BoundingBox(lesserCorner, greaterCorner), - VisualizationType.NATURE_RESTORATION_ZONE); - } - } -} diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/RestoreNatureProcessingTask.java b/src/main/java/me/ryanhamshire/GriefPrevention/RestoreNatureProcessingTask.java deleted file mode 100644 index c15891be..00000000 --- a/src/main/java/me/ryanhamshire/GriefPrevention/RestoreNatureProcessingTask.java +++ /dev/null @@ -1,875 +0,0 @@ -/* - GriefPrevention Server Plugin for Minecraft - Copyright (C) 2012 Ryan Hamshire - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - */ - -package me.ryanhamshire.GriefPrevention; - -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.NamespacedKey; -import org.bukkit.Tag; -import org.bukkit.World.Environment; -import org.bukkit.block.Biome; -import org.bukkit.block.data.Levelled; -import org.bukkit.block.data.type.Leaves; -import org.bukkit.entity.Player; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -//non-main-thread task which processes world data to repair the unnatural -//after processing is complete, creates a main thread task to make the necessary changes to the world -class RestoreNatureProcessingTask implements Runnable -{ - - // Definitions of biomes with particularly dense log distribution. These biomes will not have logs reduced. - private static final Set DENSE_LOG_BIOMES = Set.of( - NamespacedKey.minecraft("jungle"), - NamespacedKey.minecraft("bamboo_jungle") - ); - - // Definitions of biomes where sand covers surfaces instead of grass. - private static final Set SAND_SOIL_BIOMES = Set.of( - NamespacedKey.minecraft("snowy_beach"), - NamespacedKey.minecraft("beach"), - NamespacedKey.minecraft("desert") - ); - - //world information captured from the main thread - //will be updated and sent back to main thread to be applied to the world - private final BlockSnapshot[][][] snapshots; - - //other information collected from the main thread. - //not to be updated, only to be passed back to main thread to provide some context about the operation - private int miny; - private final Environment environment; - private final Location lesserBoundaryCorner; - private final Location greaterBoundaryCorner; - private final Player player; //absolutely must not be accessed. not thread safe. - private final Biome biome; - private final boolean creativeMode; - private final int seaLevel; - private final boolean aggressiveMode; - - //two lists of materials - private final Set notAllowedToHang; //natural blocks which don't naturally hang in their air - private final Set playerBlocks; //a "complete" list of player-placed blocks. MUST BE MAINTAINED as patches introduce more - - - public RestoreNatureProcessingTask(BlockSnapshot[][][] snapshots, int miny, Environment environment, Biome biome, Location lesserBoundaryCorner, Location greaterBoundaryCorner, int seaLevel, boolean aggressiveMode, boolean creativeMode, Player player) - { - this.snapshots = snapshots; - this.miny = miny; - if (this.miny < 0) this.miny = 0; - this.environment = environment; - this.lesserBoundaryCorner = lesserBoundaryCorner; - this.greaterBoundaryCorner = greaterBoundaryCorner; - this.biome = biome; - this.seaLevel = seaLevel; - this.aggressiveMode = aggressiveMode; - this.player = player; - this.creativeMode = creativeMode; - - this.notAllowedToHang = new HashSet<>(); - this.notAllowedToHang.add(Material.DIRT); - this.notAllowedToHang.add(Material.SHORT_GRASS); - this.notAllowedToHang.add(Material.SNOW); - this.notAllowedToHang.add(Material.OAK_LOG); - this.notAllowedToHang.add(Material.SPRUCE_LOG); - this.notAllowedToHang.add(Material.BIRCH_LOG); - this.notAllowedToHang.add(Material.JUNGLE_LOG); - this.notAllowedToHang.add(Material.ACACIA_LOG); - this.notAllowedToHang.add(Material.DARK_OAK_LOG); - - if (this.aggressiveMode) - { - this.notAllowedToHang.add(Material.SHORT_GRASS); - this.notAllowedToHang.add(Material.STONE); - } - - this.playerBlocks = new HashSet<>(); - this.playerBlocks.addAll(RestoreNatureProcessingTask.getPlayerBlocks(this.environment, this.biome)); - - //in aggressive or creative world mode, also treat these blocks as user placed, to be removed - //this is helpful in the few cases where griefers intentionally use natural blocks to grief, - //like a single-block tower of iron ore or a giant penis constructed with melons - if (this.aggressiveMode || this.creativeMode) - { - this.playerBlocks.addAll(Tag.COPPER_ORES.getValues()); - this.playerBlocks.addAll(Tag.IRON_ORES.getValues()); - this.playerBlocks.addAll(Tag.GOLD_ORES.getValues()); - this.playerBlocks.addAll(Tag.DIAMOND_ORES.getValues()); - this.playerBlocks.addAll(Tag.COAL_ORES.getValues()); - this.playerBlocks.add(Material.MELON); - this.playerBlocks.add(Material.MELON_STEM); - this.playerBlocks.add(Material.BEDROCK); - this.playerBlocks.add(Material.PUMPKIN); - this.playerBlocks.add(Material.PUMPKIN_STEM); - } - - if (this.aggressiveMode) - { - this.playerBlocks.addAll(Tag.LEAVES.getValues()); - this.playerBlocks.addAll(Tag.LOGS.getValues()); - this.playerBlocks.add(Material.VINE); - } - } - - @Override - public void run() - { - //order is important! - - //remove sandstone which appears to be unnatural - this.removeSandstone(); - - //remove any blocks which are definitely player placed - this.removePlayerBlocks(); - - //reduce large outcroppings of stone, sandstone - this.reduceStone(); - - //reduce logs, except in jungle biomes - this.reduceLogs(); - - //remove natural blocks which are unnaturally hanging in the air - this.removeHanging(); - - //remove natural blocks which are unnaturally stacked high - this.removeWallsAndTowers(); - - //fill unnatural thin trenches and single-block potholes - this.fillHolesAndTrenches(); - - //fill water depressions and fix unnatural surface ripples - //this.fixWater(); - - //remove water/lava above sea level - this.removeDumpedFluids(); - - //cover surface stone and gravel with sand or grass, as the biome requires - this.coverSurfaceStone(); - - //remove any player-placed leaves - ///this.removePlayerLeaves(); - - //schedule main thread task to apply the result to the world - RestoreNatureExecutionTask task = new RestoreNatureExecutionTask(this.snapshots, this.miny, this.lesserBoundaryCorner, this.greaterBoundaryCorner, this.player); - GriefPrevention.scheduler.getImpl().runAtLocation(this.lesserBoundaryCorner, task); - } - - - private void removePlayerLeaves() - { - if (this.seaLevel < 1) return; - - for (int x = 1; x < snapshots.length - 1; x++) - { - for (int z = 1; z < snapshots[0][0].length - 1; z++) - { - for (int y = this.seaLevel - 1; y < snapshots[0].length; y++) - { - BlockSnapshot block = snapshots[x][y][z]; - if (Tag.LEAVES.isTagged(block.typeId) && ((Leaves) block.data).isPersistent()) - { - block.typeId = Material.AIR; - } - } - } - } - } - - //converts sandstone adjacent to sand to sand, and any other sandstone to air - - private void removeSandstone() - { - for (int x = 1; x < snapshots.length - 1; x++) - { - for (int z = 1; z < snapshots[0][0].length - 1; z++) - { - for (int y = snapshots[0].length - 2; y > miny; y--) - { - if (snapshots[x][y][z].typeId != Material.SANDSTONE) continue; - - BlockSnapshot leftBlock = this.snapshots[x + 1][y][z]; - BlockSnapshot rightBlock = this.snapshots[x - 1][y][z]; - BlockSnapshot upBlock = this.snapshots[x][y][z + 1]; - BlockSnapshot downBlock = this.snapshots[x][y][z - 1]; - BlockSnapshot underBlock = this.snapshots[x][y - 1][z]; - BlockSnapshot aboveBlock = this.snapshots[x][y + 1][z]; - - //skip blocks which may cause a cave-in - if (aboveBlock.typeId == Material.SAND && underBlock.typeId == Material.AIR) continue; - - //count adjacent non-air/non-leaf blocks - if (leftBlock.typeId == Material.SAND || - rightBlock.typeId == Material.SAND || - upBlock.typeId == Material.SAND || - downBlock.typeId == Material.SAND || - aboveBlock.typeId == Material.SAND || - underBlock.typeId == Material.SAND) - { - snapshots[x][y][z].typeId = Material.SAND; - } - else - { - snapshots[x][y][z].typeId = Material.AIR; - } - } - } - } - } - - - private void reduceStone() - { - if (this.seaLevel < 1) return; - - for (int x = 1; x < snapshots.length - 1; x++) - { - for (int z = 1; z < snapshots[0][0].length - 1; z++) - { - int thisy = this.highestY(x, z, true); - - while (thisy > this.seaLevel - 1 && (this.snapshots[x][thisy][z].typeId == Material.STONE || this.snapshots[x][thisy][z].typeId == Material.SANDSTONE)) - { - BlockSnapshot leftBlock = this.snapshots[x + 1][thisy][z]; - BlockSnapshot rightBlock = this.snapshots[x - 1][thisy][z]; - BlockSnapshot upBlock = this.snapshots[x][thisy][z + 1]; - BlockSnapshot downBlock = this.snapshots[x][thisy][z - 1]; - - //count adjacent non-air/non-leaf blocks - byte adjacentBlockCount = 0; - if (leftBlock.typeId != Material.AIR && !Tag.LEAVES.isTagged(leftBlock.typeId) && leftBlock.typeId != Material.VINE) - { - adjacentBlockCount++; - } - if (rightBlock.typeId != Material.AIR && !Tag.LEAVES.isTagged(rightBlock.typeId) && rightBlock.typeId != Material.VINE) - { - adjacentBlockCount++; - } - if (downBlock.typeId != Material.AIR && !Tag.LEAVES.isTagged(downBlock.typeId) && downBlock.typeId != Material.VINE) - { - adjacentBlockCount++; - } - if (upBlock.typeId != Material.AIR && !Tag.LEAVES.isTagged(upBlock.typeId) && upBlock.typeId != Material.VINE) - { - adjacentBlockCount++; - } - - if (adjacentBlockCount < 3) - { - this.snapshots[x][thisy][z].typeId = Material.AIR; - } - - thisy--; - } - } - } - } - - - private void reduceLogs() - { - if (this.seaLevel < 1) return; - - boolean jungleBiome = DENSE_LOG_BIOMES.contains(this.biome.getKey()); - - //scan all blocks above sea level - for (int x = 1; x < snapshots.length - 1; x++) - { - for (int z = 1; z < snapshots[0][0].length - 1; z++) - { - for (int y = this.seaLevel - 1; y < snapshots[0].length; y++) - { - BlockSnapshot block = snapshots[x][y][z]; - - //skip non-logs - if (!Tag.LOGS.isTagged(block.typeId)) continue; - - //if in jungle biome, skip jungle logs - if (jungleBiome && block.typeId == Material.JUNGLE_LOG) continue; - - //examine adjacent blocks for logs - BlockSnapshot leftBlock = this.snapshots[x + 1][y][z]; - BlockSnapshot rightBlock = this.snapshots[x - 1][y][z]; - BlockSnapshot upBlock = this.snapshots[x][y][z + 1]; - BlockSnapshot downBlock = this.snapshots[x][y][z - 1]; - - //if any, remove the log - if (Tag.LOGS.isTagged(leftBlock.typeId) || Tag.LOGS.isTagged(rightBlock.typeId) || Tag.LOGS.isTagged(upBlock.typeId) || Tag.LOGS.isTagged(downBlock.typeId)) - { - this.snapshots[x][y][z].typeId = Material.AIR; - } - } - } - } - } - - - private void removePlayerBlocks() - { - int miny = this.miny; - if (miny < 1) miny = 1; - - //remove all player blocks - for (int x = 1; x < snapshots.length - 1; x++) - { - for (int z = 1; z < snapshots[0][0].length - 1; z++) - { - for (int y = miny; y < snapshots[0].length - 1; y++) - { - BlockSnapshot block = snapshots[x][y][z]; - - if (this.playerBlocks.contains(block.typeId)) - { - block.typeId = Material.AIR; - } - } - } - } - } - - - private void removeHanging() - { - int miny = this.miny; - if (miny < 1) miny = 1; - - for (int x = 1; x < snapshots.length - 1; x++) - { - for (int z = 1; z < snapshots[0][0].length - 1; z++) - { - for (int y = miny; y < snapshots[0].length - 1; y++) - { - BlockSnapshot block = snapshots[x][y][z]; - BlockSnapshot underBlock = snapshots[x][y - 1][z]; - - if (underBlock.typeId == Material.AIR || underBlock.typeId == Material.WATER || Tag.LEAVES.isTagged(underBlock.typeId)) - { - if (this.notAllowedToHang.contains(block.typeId)) - { - block.typeId = Material.AIR; - } - } - } - } - } - } - - - private void removeWallsAndTowers() - { - Material[] excludedBlocksArray = new Material[] - { - Material.CACTUS, - Material.SHORT_GRASS, - Material.RED_MUSHROOM, - Material.BROWN_MUSHROOM, - Material.DEAD_BUSH, - Material.DANDELION, - Material.POPPY, - Material.ALLIUM, - Material.BLUE_ORCHID, - Material.AZURE_BLUET, - Material.RED_TULIP, - Material.ORANGE_TULIP, - Material.WHITE_TULIP, - Material.PINK_TULIP, - Material.OXEYE_DAISY, - Material.SUGAR_CANE, - Material.VINE, - Material.PUMPKIN, - Material.LILY_PAD - }; - - ArrayList excludedBlocks = new ArrayList<>(Arrays.asList(excludedBlocksArray)); - - excludedBlocks.addAll(Tag.SAPLINGS.getValues()); - excludedBlocks.addAll(Tag.LEAVES.getValues()); - - boolean changed; - do - { - changed = false; - for (int x = 1; x < snapshots.length - 1; x++) - { - for (int z = 1; z < snapshots[0][0].length - 1; z++) - { - int thisy = this.highestY(x, z, false); - if (excludedBlocks.contains(this.snapshots[x][thisy][z].typeId)) continue; - - int righty = this.highestY(x + 1, z, false); - int lefty = this.highestY(x - 1, z, false); - while (lefty < thisy && righty < thisy) - { - this.snapshots[x][thisy--][z].typeId = Material.AIR; - changed = true; - } - - int upy = this.highestY(x, z + 1, false); - int downy = this.highestY(x, z - 1, false); - while (upy < thisy && downy < thisy) - { - this.snapshots[x][thisy--][z].typeId = Material.AIR; - changed = true; - } - } - } - } while (changed); - } - - - private void coverSurfaceStone() - { - for (int x = 1; x < snapshots.length - 1; x++) - { - for (int z = 1; z < snapshots[0][0].length - 1; z++) - { - int y = this.highestY(x, z, true); - BlockSnapshot block = snapshots[x][y][z]; - - if (block.typeId == Material.STONE || block.typeId == Material.GRAVEL || block.typeId == Material.FARMLAND || block.typeId == Material.DIRT || block.typeId == Material.SANDSTONE) - { - if (SAND_SOIL_BIOMES.contains(this.biome.getKey())) - { - this.snapshots[x][y][z].typeId = Material.SAND; - } - else - { - this.snapshots[x][y][z].typeId = Material.GRASS_BLOCK; - } - } - } - } - } - - - private void fillHolesAndTrenches() - { - ArrayList fillableBlocks = new ArrayList<>(); - fillableBlocks.add(Material.AIR); - fillableBlocks.add(Material.WATER); - fillableBlocks.add(Material.LAVA); - fillableBlocks.add(Material.SHORT_GRASS); - - ArrayList notSuitableForFillBlocks = new ArrayList<>(); - notSuitableForFillBlocks.add(Material.SHORT_GRASS); - notSuitableForFillBlocks.add(Material.CACTUS); - notSuitableForFillBlocks.add(Material.WATER); - notSuitableForFillBlocks.add(Material.LAVA); - notSuitableForFillBlocks.addAll(Tag.LOGS.getValues()); - - boolean changed; - do - { - changed = false; - for (int x = 1; x < snapshots.length - 1; x++) - { - for (int z = 1; z < snapshots[0][0].length - 1; z++) - { - for (int y = 0; y < snapshots[0].length - 1; y++) - { - BlockSnapshot block = this.snapshots[x][y][z]; - if (!fillableBlocks.contains(block.typeId)) continue; - - BlockSnapshot leftBlock = this.snapshots[x + 1][y][z]; - BlockSnapshot rightBlock = this.snapshots[x - 1][y][z]; - - if (!fillableBlocks.contains(leftBlock.typeId) && !fillableBlocks.contains(rightBlock.typeId)) - { - if (!notSuitableForFillBlocks.contains(rightBlock.typeId)) - { - block.typeId = rightBlock.typeId; - changed = true; - } - } - - BlockSnapshot upBlock = this.snapshots[x][y][z + 1]; - BlockSnapshot downBlock = this.snapshots[x][y][z - 1]; - - if (!fillableBlocks.contains(upBlock.typeId) && !fillableBlocks.contains(downBlock.typeId)) - { - if (!notSuitableForFillBlocks.contains(downBlock.typeId)) - { - block.typeId = downBlock.typeId; - changed = true; - } - } - } - } - } - } while (changed); - } - - - private void fixWater() - { - int miny = this.miny; - if (miny < 1) miny = 1; - - boolean changed; - - //remove hanging water or lava - for (int x = 1; x < snapshots.length - 1; x++) - { - for (int z = 1; z < snapshots[0][0].length - 1; z++) - { - for (int y = miny; y < snapshots[0].length - 1; y++) - { - BlockSnapshot block = this.snapshots[x][y][z]; - BlockSnapshot underBlock = this.snapshots[x][y--][z]; - if (block.typeId == Material.WATER || block.typeId == Material.LAVA) - { - // check if block below is air or is a non-source fluid block (level 1-7 = flowing, 8 = falling) - if (underBlock.typeId == Material.AIR || (underBlock.typeId == Material.WATER && (((Levelled) underBlock.data).getLevel() != 0))) - { - block.typeId = Material.AIR; - } - } - } - } - } - - //fill water depressions - do - { - changed = false; - for (int y = Math.max(this.seaLevel - 10, 0); y <= this.seaLevel; y++) - { - for (int x = 1; x < snapshots.length - 1; x++) - { - for (int z = 1; z < snapshots[0][0].length - 1; z++) - { - BlockSnapshot block = snapshots[x][y][z]; - - //only consider air blocks and flowing water blocks for upgrade to water source blocks - if (block.typeId == Material.AIR || (block.typeId == Material.WATER && ((Levelled) block.data).getLevel() != 0)) - { - BlockSnapshot leftBlock = this.snapshots[x + 1][y][z]; - BlockSnapshot rightBlock = this.snapshots[x - 1][y][z]; - BlockSnapshot upBlock = this.snapshots[x][y][z + 1]; - BlockSnapshot downBlock = this.snapshots[x][y][z - 1]; - BlockSnapshot underBlock = this.snapshots[x][y - 1][z]; - - //block underneath MUST be source water - if (!(underBlock.typeId == Material.WATER && ((Levelled) underBlock.data).getLevel() == 0)) - continue; - - //count adjacent source water blocks - byte adjacentSourceWaterCount = 0; - if (leftBlock.typeId == Material.WATER && ((Levelled) leftBlock.data).getLevel() == 0) - { - adjacentSourceWaterCount++; - } - if (rightBlock.typeId == Material.WATER && ((Levelled) rightBlock.data).getLevel() == 0) - { - adjacentSourceWaterCount++; - } - if (upBlock.typeId == Material.WATER && ((Levelled) upBlock.data).getLevel() == 0) - { - adjacentSourceWaterCount++; - } - if (downBlock.typeId == Material.WATER && ((Levelled) downBlock.data).getLevel() == 0) - { - adjacentSourceWaterCount++; - } - - //at least two adjacent blocks must be source water - if (adjacentSourceWaterCount >= 2) - { - block.typeId = Material.WATER; - ((Levelled) downBlock.data).setLevel(0); - changed = true; - } - } - } - } - } - } while (changed); - } - - - private void removeDumpedFluids() - { - if (this.seaLevel < 1) return; - - //remove any surface water or lava above sea level, presumed to be placed by players - //sometimes, this is naturally generated. but replacing it is very easy with a bucket, so overall this is a good plan - if (this.environment == Environment.NETHER) return; - for (int x = 1; x < snapshots.length - 1; x++) - { - for (int z = 1; z < snapshots[0][0].length - 1; z++) - { - for (int y = this.seaLevel; y < snapshots[0].length - 1; y++) - { - BlockSnapshot block = snapshots[x][y][z]; - if (block.typeId == Material.WATER || block.typeId == Material.LAVA) - { - block.typeId = Material.AIR; - } - } - } - } - } - - - private int highestY(int x, int z, boolean ignoreLeaves) - { - int y; - for (y = snapshots[0].length - 1; y > 0; y--) - { - BlockSnapshot block = this.snapshots[x][y][z]; - if (block.typeId != Material.AIR && - !(ignoreLeaves && block.typeId == Material.SNOW) && - !(ignoreLeaves && Tag.LEAVES.isTagged(block.typeId)) && - !(block.typeId == Material.WATER) && - !(block.typeId == Material.LAVA)) - { - return y; - } - } - - return y; - } - - - static Set getPlayerBlocks(Environment environment, Biome biome) - { - //NOTE on this list. why not make a list of natural blocks? - //answer: better to leave a few player blocks than to remove too many natural blocks. remember we're "restoring nature" - //a few extra player blocks can be manually removed, but it will be impossible to guess exactly which natural materials to use in manual repair of an overzealous block removal - Set playerBlocks = new HashSet<>(); - playerBlocks.addAll(Tag.ANVIL.getValues()); - playerBlocks.addAll(Tag.BANNERS.getValues()); - playerBlocks.addAll(Tag.BEACON_BASE_BLOCKS.getValues()); - playerBlocks.addAll(Tag.BEDS.getValues()); - playerBlocks.addAll(Tag.BUTTONS.getValues()); - playerBlocks.addAll(Tag.CAMPFIRES.getValues()); - playerBlocks.addAll(Tag.CANDLE_CAKES.getValues()); - playerBlocks.addAll(Tag.CANDLES.getValues()); - playerBlocks.addAll(Tag.WOOL_CARPETS.getValues()); - playerBlocks.addAll(Tag.CAULDRONS.getValues()); - playerBlocks.addAll(Tag.DOORS.getValues()); - playerBlocks.addAll(Tag.FENCE_GATES.getValues()); - playerBlocks.addAll(Tag.FENCES.getValues()); - playerBlocks.addAll(Tag.FIRE.getValues()); - playerBlocks.addAll(Tag.FLOWER_POTS.getValues()); - playerBlocks.addAll(Tag.IMPERMEABLE.getValues()); // Glass block variants - playerBlocks.addAll(Tag.LOGS.getValues()); - playerBlocks.addAll(Tag.PLANKS.getValues()); - playerBlocks.addAll(Tag.PRESSURE_PLATES.getValues()); - playerBlocks.addAll(Tag.RAILS.getValues()); - playerBlocks.addAll(Tag.SHULKER_BOXES.getValues()); - playerBlocks.addAll(Tag.SIGNS.getValues()); - playerBlocks.addAll(Tag.SLABS.getValues()); - playerBlocks.addAll(Tag.STAIRS.getValues()); - playerBlocks.addAll(Tag.STONE_BRICKS.getValues()); - playerBlocks.addAll(Tag.TRAPDOORS.getValues()); - playerBlocks.addAll(Tag.WALLS.getValues()); - playerBlocks.addAll(Tag.WOOL.getValues()); - playerBlocks.add(Material.BOOKSHELF); - playerBlocks.add(Material.BREWING_STAND); - playerBlocks.add(Material.BRICK); - playerBlocks.add(Material.COBBLESTONE); - playerBlocks.add(Material.LAPIS_BLOCK); - playerBlocks.add(Material.DISPENSER); - playerBlocks.add(Material.NOTE_BLOCK); - playerBlocks.add(Material.STICKY_PISTON); - playerBlocks.add(Material.PISTON); - playerBlocks.add(Material.PISTON_HEAD); - playerBlocks.add(Material.MOVING_PISTON); - playerBlocks.add(Material.WHEAT); - playerBlocks.add(Material.TNT); - playerBlocks.add(Material.MOSSY_COBBLESTONE); - playerBlocks.add(Material.TORCH); - playerBlocks.add(Material.CHEST); - playerBlocks.add(Material.REDSTONE_WIRE); - playerBlocks.add(Material.CRAFTING_TABLE); - playerBlocks.add(Material.FURNACE); - playerBlocks.add(Material.LADDER); - playerBlocks.add(Material.SCAFFOLDING); - playerBlocks.add(Material.LEVER); - playerBlocks.add(Material.REDSTONE_TORCH); - playerBlocks.add(Material.SNOW_BLOCK); - playerBlocks.add(Material.JUKEBOX); - playerBlocks.add(Material.NETHER_PORTAL); - playerBlocks.add(Material.JACK_O_LANTERN); - playerBlocks.add(Material.CAKE); - playerBlocks.add(Material.REPEATER); - playerBlocks.add(Material.MUSHROOM_STEM); - playerBlocks.add(Material.RED_MUSHROOM_BLOCK); - playerBlocks.add(Material.BROWN_MUSHROOM_BLOCK); - playerBlocks.add(Material.IRON_BARS); - playerBlocks.add(Material.GLASS_PANE); - playerBlocks.add(Material.MELON_STEM); - playerBlocks.add(Material.ENCHANTING_TABLE); - playerBlocks.add(Material.COBWEB); - playerBlocks.add(Material.GRAVEL); - playerBlocks.add(Material.SANDSTONE); - playerBlocks.add(Material.ENDER_CHEST); - playerBlocks.add(Material.COMMAND_BLOCK); - playerBlocks.add(Material.REPEATING_COMMAND_BLOCK); - playerBlocks.add(Material.CHAIN_COMMAND_BLOCK); - playerBlocks.add(Material.BEACON); - playerBlocks.add(Material.CARROT); - playerBlocks.add(Material.POTATO); - playerBlocks.add(Material.SKELETON_SKULL); - playerBlocks.add(Material.WITHER_SKELETON_SKULL); - playerBlocks.add(Material.CREEPER_HEAD); - playerBlocks.add(Material.ZOMBIE_HEAD); - playerBlocks.add(Material.PLAYER_HEAD); - playerBlocks.add(Material.DRAGON_HEAD); - playerBlocks.add(Material.SPONGE); - playerBlocks.add(Material.WHITE_STAINED_GLASS_PANE); - playerBlocks.add(Material.ORANGE_STAINED_GLASS_PANE); - playerBlocks.add(Material.MAGENTA_STAINED_GLASS_PANE); - playerBlocks.add(Material.LIGHT_BLUE_STAINED_GLASS_PANE); - playerBlocks.add(Material.YELLOW_STAINED_GLASS_PANE); - playerBlocks.add(Material.LIME_STAINED_GLASS_PANE); - playerBlocks.add(Material.PINK_STAINED_GLASS_PANE); - playerBlocks.add(Material.GRAY_STAINED_GLASS_PANE); - playerBlocks.add(Material.LIGHT_GRAY_STAINED_GLASS_PANE); - playerBlocks.add(Material.CYAN_STAINED_GLASS_PANE); - playerBlocks.add(Material.PURPLE_STAINED_GLASS_PANE); - playerBlocks.add(Material.BLUE_STAINED_GLASS_PANE); - playerBlocks.add(Material.BROWN_STAINED_GLASS_PANE); - playerBlocks.add(Material.GREEN_STAINED_GLASS_PANE); - playerBlocks.add(Material.RED_STAINED_GLASS_PANE); - playerBlocks.add(Material.BLACK_STAINED_GLASS_PANE); - playerBlocks.add(Material.TRAPPED_CHEST); - playerBlocks.add(Material.COMPARATOR); - playerBlocks.add(Material.DAYLIGHT_DETECTOR); - playerBlocks.add(Material.REDSTONE_BLOCK); - playerBlocks.add(Material.HOPPER); - playerBlocks.add(Material.QUARTZ_BLOCK); - playerBlocks.add(Material.DROPPER); - playerBlocks.add(Material.SLIME_BLOCK); - playerBlocks.add(Material.PRISMARINE); - playerBlocks.add(Material.HAY_BLOCK); - playerBlocks.add(Material.SEA_LANTERN); - playerBlocks.add(Material.COAL_BLOCK); - playerBlocks.add(Material.REDSTONE_LAMP); - playerBlocks.add(Material.RED_NETHER_BRICKS); - playerBlocks.add(Material.POLISHED_ANDESITE); - playerBlocks.add(Material.POLISHED_DIORITE); - playerBlocks.add(Material.POLISHED_GRANITE); - playerBlocks.add(Material.POLISHED_BASALT); - playerBlocks.add(Material.POLISHED_DEEPSLATE); - playerBlocks.add(Material.DEEPSLATE_BRICKS); - playerBlocks.add(Material.CRACKED_DEEPSLATE_BRICKS); - playerBlocks.add(Material.DEEPSLATE_TILES); - playerBlocks.add(Material.CRACKED_DEEPSLATE_TILES); - playerBlocks.add(Material.CHISELED_DEEPSLATE); - playerBlocks.add(Material.RAW_COPPER_BLOCK); - playerBlocks.add(Material.RAW_IRON_BLOCK); - playerBlocks.add(Material.RAW_GOLD_BLOCK); - playerBlocks.add(Material.LIGHTNING_ROD); - playerBlocks.add(Material.DECORATED_POT); - - //these are unnatural in the nether and end - if (environment != Environment.NORMAL && environment != Environment.CUSTOM) - { - playerBlocks.addAll(Tag.BASE_STONE_OVERWORLD.getValues()); - playerBlocks.addAll(Tag.DIRT.getValues()); - playerBlocks.addAll(Tag.SAND.getValues()); - } - - //these are unnatural in the standard world, but not in the nether - if (environment != Environment.NETHER) - { - playerBlocks.addAll(Tag.NYLIUM.getValues()); - playerBlocks.addAll(Tag.WART_BLOCKS.getValues()); - playerBlocks.addAll(Tag.BASE_STONE_NETHER.getValues()); - playerBlocks.add(Material.POLISHED_BLACKSTONE); - playerBlocks.add(Material.CHISELED_POLISHED_BLACKSTONE); - playerBlocks.add(Material.CRACKED_POLISHED_BLACKSTONE_BRICKS); - playerBlocks.add(Material.GILDED_BLACKSTONE); - playerBlocks.add(Material.BONE_BLOCK); - playerBlocks.add(Material.SOUL_SAND); - playerBlocks.add(Material.SOUL_SOIL); - playerBlocks.add(Material.GLOWSTONE); - playerBlocks.add(Material.NETHER_BRICK); - playerBlocks.add(Material.MAGMA_BLOCK); - playerBlocks.add(Material.ANCIENT_DEBRIS); - playerBlocks.add(Material.CHAIN); - playerBlocks.add(Material.SHROOMLIGHT); - playerBlocks.add(Material.NETHER_GOLD_ORE); - playerBlocks.add(Material.NETHER_SPROUTS); - playerBlocks.add(Material.CRIMSON_FUNGUS); - playerBlocks.add(Material.CRIMSON_ROOTS); - playerBlocks.add(Material.NETHER_WART_BLOCK); - playerBlocks.add(Material.WEEPING_VINES); - playerBlocks.add(Material.WEEPING_VINES_PLANT); - playerBlocks.add(Material.WARPED_FUNGUS); - playerBlocks.add(Material.WARPED_ROOTS); - playerBlocks.add(Material.WARPED_WART_BLOCK); - playerBlocks.add(Material.TWISTING_VINES); - playerBlocks.add(Material.TWISTING_VINES_PLANT); - } - //blocks from tags that are natural in the nether - else - { - playerBlocks.remove(Material.CRIMSON_STEM); - playerBlocks.remove(Material.CRIMSON_HYPHAE); - playerBlocks.remove(Material.NETHER_BRICK_FENCE); - playerBlocks.remove(Material.NETHER_BRICK_STAIRS); - playerBlocks.remove(Material.SOUL_FIRE); - playerBlocks.remove(Material.WARPED_STEM); - playerBlocks.remove(Material.WARPED_HYPHAE); - } - - //these are unnatural in the standard and nether worlds, but not in the end - if (environment != Environment.THE_END) - { - playerBlocks.add(Material.CHORUS_PLANT); - playerBlocks.add(Material.CHORUS_FLOWER); - playerBlocks.add(Material.END_ROD); - playerBlocks.add(Material.END_STONE); - playerBlocks.add(Material.END_STONE_BRICKS); - playerBlocks.add(Material.OBSIDIAN); - playerBlocks.add(Material.PURPUR_BLOCK); - playerBlocks.add(Material.PURPUR_PILLAR); - } - //blocks from tags that are natural in the end - else - { - playerBlocks.remove(Material.PURPUR_SLAB); - playerBlocks.remove(Material.PURPUR_STAIRS); - } - - //these are unnatural in sandy biomes, but not elsewhere - if (SAND_SOIL_BIOMES.contains(biome.getKey()) || environment != Environment.NORMAL) - { - playerBlocks.addAll(Tag.LEAVES.getValues()); - } - //blocks from tags that are natural in non-sandy normal biomes - else - { - playerBlocks.remove(Material.OAK_LOG); - playerBlocks.remove(Material.SPRUCE_LOG); - playerBlocks.remove(Material.BIRCH_LOG); - playerBlocks.remove(Material.JUNGLE_LOG); - playerBlocks.remove(Material.ACACIA_LOG); - playerBlocks.remove(Material.DARK_OAK_LOG); - } - - return playerBlocks; - } -} diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/ShovelMode.java b/src/main/java/me/ryanhamshire/GriefPrevention/ShovelMode.java index 3985be47..af493016 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/ShovelMode.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/ShovelMode.java @@ -23,8 +23,5 @@ public enum ShovelMode { Basic, Admin, - Subdivide, - RestoreNature, - RestoreNatureAggressive, - RestoreNatureFill + Subdivide } diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/VisualizationType.java b/src/main/java/me/ryanhamshire/GriefPrevention/VisualizationType.java index 669e01e1..88aa9fb9 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/VisualizationType.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/VisualizationType.java @@ -29,7 +29,6 @@ public enum VisualizationType Claim, Subdivision, ErrorClaim, - RestoreNature, AdminClaim; @Deprecated(forRemoval = true, since = "16.18") @@ -40,7 +39,6 @@ com.griefprevention.visualization.VisualizationType convert() case Claim -> com.griefprevention.visualization.VisualizationType.CLAIM; case Subdivision -> com.griefprevention.visualization.VisualizationType.SUBDIVISION; case ErrorClaim -> com.griefprevention.visualization.VisualizationType.CONFLICT_ZONE; - case RestoreNature -> com.griefprevention.visualization.VisualizationType.NATURE_RESTORATION_ZONE; case AdminClaim -> com.griefprevention.visualization.VisualizationType.ADMIN_CLAIM; }; } @@ -50,7 +48,6 @@ static com.griefprevention.visualization.VisualizationType ofBlockData(BlockData return switch (accent.getMaterial()) { case WHITE_WOOL -> com.griefprevention.visualization.VisualizationType.SUBDIVISION; case NETHERRACK -> com.griefprevention.visualization.VisualizationType.CONFLICT_ZONE; - case DIAMOND_BLOCK -> com.griefprevention.visualization.VisualizationType.NATURE_RESTORATION_ZONE; case PUMPKIN -> com.griefprevention.visualization.VisualizationType.ADMIN_CLAIM; default -> com.griefprevention.visualization.VisualizationType.CLAIM; }; diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/events/PreventBlockBreakEvent.java b/src/main/java/me/ryanhamshire/GriefPrevention/events/PreventBlockBreakEvent.java index fee8190f..03e4c72b 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/events/PreventBlockBreakEvent.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/events/PreventBlockBreakEvent.java @@ -7,9 +7,10 @@ import org.jetbrains.annotations.NotNull; /** - * An {@link Event} called when GriefPrevention prevents a {@link BlockBreakEvent}. - * If cancelled, GriefPrevention will allow the event to complete normally. + * @deprecated Listen to {@link ClaimPermissionCheckEvent} and check if + * {@link ClaimPermissionCheckEvent#getTriggeringEvent()} {@code instanceof} {@link BlockBreakEvent}. */ +@Deprecated(forRemoval = true, since = "17.0.0") public class PreventBlockBreakEvent extends Event implements Cancellable { private final @NotNull BlockBreakEvent innerEvent; diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 042f6346..d5bc1e02 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -95,21 +95,6 @@ commands: usage: / permission: griefprevention.adminclaims aliases: ac - restorenature: - description: Switches the shovel tool to restoration mode. - usage: / - permission: griefprevention.restorenature - aliases: rn - restorenatureaggressive: - description: Switches the shovel tool to aggressive restoration mode. - usage: / - permission: griefprevention.restorenatureaggressive - aliases: rna - restorenaturefill: - description: Switches the shovel tool to fill mode. - usage: / - permission: griefprevention.restorenatureaggressive - aliases: rnf basicclaims: description: Switches the shovel tool back to basic claims mode. usage: / @@ -173,14 +158,6 @@ commands: description: Reloads Grief Prevention's configuration settings. Does NOT totally reload the entire plugin. usage: / permission: griefprevention.reload - givepet: - description: Allows a player to give away a pet he or she tamed. - usage: / - permission: griefprevention.givepet - gpblockinfo: - description: Allows an administrator to get technical information about blocks in the world and items in hand. - usage: / - permission: griefprevention.gpblockinfo ignoreplayer: description: Ignores another player's chat messages. usage: / @@ -215,8 +192,6 @@ permissions: griefprevention.admin.*: description: Grants all administrative functionality. children: - griefprevention.restorenature: true - griefprevention.restorenatureaggressive: true griefprevention.ignoreclaims: true griefprevention.adminclaims: true griefprevention.adjustclaimblocks: true @@ -240,9 +215,6 @@ permissions: griefprevention.deleteclaimsinworld: true griefprevention.unlockothersdrops: true griefprevention.seeclaimsize: true - griefprevention.givepet: - description: Grants permission to use /givepet. - default: true griefprevention.unlockdrops: description: Grants permission to use /unlockdrops. default: true @@ -255,9 +227,6 @@ permissions: griefprevention.claimslistother: description: Grants permission to use /claimslist to get another player's information. default: op - griefprevention.restorenature: - description: Grants permission to use /restorenature. - default: op griefprevention.transferclaim: description: Grants permission to use /transferclaim. default: op @@ -285,9 +254,6 @@ permissions: griefprevention.eavesdropsigns: description: Allows a player to see sign placements as chat messages. default: op - griefprevention.restorenatureaggressive: - description: Grants access to /restorenatureaggressive and /restorenaturefill. - default: op griefprevention.reload: description: Grants access to /gpreload. default: op @@ -306,9 +272,6 @@ permissions: griefprevention.seeclaimsize: description: Allows a player to see claim size for other players claims when right clicking with investigation tool default: op - griefprevention.gpblockinfo: - description: Grants access to /gpblockinfo. - default: op griefprevention.overrideclaimcountlimit: description: Allows players to create more claims than the limit specified by the config. default: op