diff --git a/build.gradle.kts b/build.gradle.kts index 91ba124..26548b0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } repositories { - mavenLocal() +// mavenLocal() maven { url = uri("https://papermc.io/repo/repository/maven-public/") } @@ -39,7 +39,7 @@ repositories { } dependencies { - compileOnly("com.alttd:Galaxy-API:1.18.2-R0.1-SNAPSHOT") + compileOnly("com.alttd:Galaxy-API:1.20.4-R0.1-SNAPSHOT") compileOnly("de.keyle:mypet:3.11-SNAPSHOT") compileOnly("com.github.NeumimTo:Pl3xMap:1.18-2") testImplementation("org.junit.jupiter:junit-jupiter:5.7.0") @@ -51,7 +51,7 @@ dependencies { group = "com.griefprevention" version = "16.18-RC2-SNAPSHOT" description = "GriefPrevention" -java.sourceCompatibility = JavaVersion.VERSION_16 +java.sourceCompatibility = JavaVersion.VERSION_17 publishing { publications.create("maven") { diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/BlockEventHandler.java b/src/main/java/me/ryanhamshire/GriefPrevention/BlockEventHandler.java index f426199..c4b3459 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/BlockEventHandler.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/BlockEventHandler.java @@ -411,7 +411,7 @@ public class BlockEventHandler implements Listener else if (Tag.SAPLINGS.isTagged(block.getType()) && GriefPrevention.instance.config_blockSkyTrees && GriefPrevention.instance.claimsEnabledForWorld(player.getWorld())) { Block earthBlock = placeEvent.getBlockAgainst(); - if (earthBlock.getType() != Material.GRASS) + if (earthBlock.getType() != Material.SHORT_GRASS) { if (earthBlock.getRelative(BlockFace.DOWN).getType() == Material.AIR || earthBlock.getRelative(BlockFace.DOWN).getRelative(BlockFace.DOWN).getType() == Material.AIR) diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/GriefPrevention.java b/src/main/java/me/ryanhamshire/GriefPrevention/GriefPrevention.java index 6121d00..e552385 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/GriefPrevention.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/GriefPrevention.java @@ -3239,26 +3239,26 @@ public class GriefPrevention extends JavaPlugin return true; } - static void banPlayer(Player player, String reason, String source) - { - if (GriefPrevention.instance.config_ban_useCommand) - { - Bukkit.getServer().dispatchCommand( - Bukkit.getConsoleSender(), - GriefPrevention.instance.config_ban_commandFormat.replace("%name%", player.getName()).replace("%reason%", reason)); - } - else - { - BanList bans = Bukkit.getServer().getBanList(Type.NAME); - bans.addBan(player.getName(), reason, null, source); - - //kick - if (player.isOnline()) - { - player.kickPlayer(reason); - } - } - } +// static void banPlayer(Player player, String reason, String source) +// { +// if (GriefPrevention.instance.config_ban_useCommand) +// { +// Bukkit.getServer().dispatchCommand( +// Bukkit.getConsoleSender(), +// GriefPrevention.instance.config_ban_commandFormat.replace("%name%", player.getName()).replace("%reason%", reason)); +// } +// else +// { +// BanList bans = Bukkit.getServer().getBanList(Type.NAME); +// bans.addBan(player.getName(), reason, null, source); +// +// //kick +// if (player.isOnline()) +// { +// player.kickPlayer(reason); +// } +// } +// } public ItemStack getItemInHand(Player player, EquipmentSlot hand) { diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java b/src/main/java/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java index 18f9ca8..97f3726 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java @@ -1560,7 +1560,7 @@ class PlayerEventHandler implements Listener } else { - allowedFillBlocks.add(Material.GRASS); + allowedFillBlocks.add(Material.SHORT_GRASS); allowedFillBlocks.add(Material.DIRT); allowedFillBlocks.add(Material.STONE); allowedFillBlocks.add(Material.SAND); @@ -1624,7 +1624,7 @@ class PlayerEventHandler implements Listener } //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.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) @@ -2066,7 +2066,7 @@ class PlayerEventHandler implements Listener Material type = result.getType(); if (type != Material.AIR && (!passThroughWater || type != Material.WATER) && - type != Material.GRASS && + type != Material.SHORT_GRASS && type != Material.SNOW) return result; } diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/PlayerKickBanTask.java b/src/main/java/me/ryanhamshire/GriefPrevention/PlayerKickBanTask.java index b82dfc2..58f551f 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/PlayerKickBanTask.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/PlayerKickBanTask.java @@ -1,71 +1,72 @@ -/* - 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 me.ryanhamshire.GriefPrevention.events.PlayerKickBanEvent; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; - -//kicks or bans a player -//need a task for this because async threads (like the chat event handlers) can't kick or ban. -//but they CAN schedule a task to run in the main thread to do that job -class PlayerKickBanTask implements Runnable -{ - //player to kick or ban - private final Player player; - - //message to send player. - private final String reason; - - //source of ban - private final String source; - - //whether to ban - private final boolean ban; - - public PlayerKickBanTask(Player player, String reason, String source, boolean ban) - { - this.player = player; - this.reason = reason; - this.source = source; - this.ban = ban; - } - - @Override - public void run() - { - PlayerKickBanEvent kickBanEvent = new PlayerKickBanEvent(player, reason, source, ban); - Bukkit.getPluginManager().callEvent(kickBanEvent); - - if (kickBanEvent.isCancelled()) - { - return; // cancelled by a plugin - } - - if (this.ban) - { - //ban - GriefPrevention.banPlayer(this.player, this.reason, this.source); - } - else if (this.player.isOnline()) - { - this.player.kickPlayer(this.reason); - } - } -} +/* + 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 me.ryanhamshire.GriefPrevention.events.PlayerKickBanEvent; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +//kicks or bans a player +//need a task for this because async threads (like the chat event handlers) can't kick or ban. +//but they CAN schedule a task to run in the main thread to do that job +class PlayerKickBanTask implements Runnable +{ + //player to kick or ban + private final Player player; + + //message to send player. + private final String reason; + + //source of ban + private final String source; + + //whether to ban + private final boolean ban; + + public PlayerKickBanTask(Player player, String reason, String source, boolean ban) + { + this.player = player; + this.reason = reason; + this.source = source; + this.ban = ban; + } + + @Override + public void run() + { + PlayerKickBanEvent kickBanEvent = new PlayerKickBanEvent(player, reason, source, ban); + Bukkit.getPluginManager().callEvent(kickBanEvent); + + if (kickBanEvent.isCancelled()) + { + return; // cancelled by a plugin + } + + if (this.ban) + { + //ban +// GriefPrevention.banPlayer(this.player, this.reason, this.source); + this.player.kickPlayer(this.reason); + } + else if (this.player.isOnline()) + { + this.player.kickPlayer(this.reason); + } + } +} diff --git a/src/main/java/me/ryanhamshire/GriefPrevention/RestoreNatureProcessingTask.java b/src/main/java/me/ryanhamshire/GriefPrevention/RestoreNatureProcessingTask.java index 723cbda..d3639ec 100644 --- a/src/main/java/me/ryanhamshire/GriefPrevention/RestoreNatureProcessingTask.java +++ b/src/main/java/me/ryanhamshire/GriefPrevention/RestoreNatureProcessingTask.java @@ -1,890 +1,890 @@ -/* - 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.EnumSet; -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"), - // Variants for versions < 1.18 - NamespacedKey.minecraft("modified_jungle"), - NamespacedKey.minecraft("jungle_hills"), - NamespacedKey.minecraft("bamboo_jungle_hills") - ); - - // 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"), - // Variants for versions < 1.18 - NamespacedKey.minecraft("desert_hills"), - NamespacedKey.minecraft("desert_lakes") - ); - - //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 = EnumSet.noneOf(Material.class); - this.notAllowedToHang.add(Material.DIRT); - this.notAllowedToHang.add(Material.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.GRASS); - this.notAllowedToHang.add(Material.STONE); - } - - this.playerBlocks = EnumSet.noneOf(Material.class); - 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.add(Material.IRON_ORE); - this.playerBlocks.add(Material.GOLD_ORE); - this.playerBlocks.add(Material.DIAMOND_ORE); - this.playerBlocks.add(Material.MELON); - this.playerBlocks.add(Material.MELON_STEM); - this.playerBlocks.add(Material.BEDROCK); - this.playerBlocks.add(Material.COAL_ORE); - this.playerBlocks.add(Material.PUMPKIN); - this.playerBlocks.add(Material.PUMPKIN_STEM); - } - - if (this.aggressiveMode) - { - this.playerBlocks.add(Material.OAK_LEAVES); - this.playerBlocks.add(Material.SPRUCE_LEAVES); - this.playerBlocks.add(Material.BIRCH_LEAVES); - this.playerBlocks.add(Material.JUNGLE_LEAVES); - this.playerBlocks.add(Material.ACACIA_LEAVES); - this.playerBlocks.add(Material.DARK_OAK_LEAVES); - this.playerBlocks.add(Material.OAK_LOG); - this.playerBlocks.add(Material.SPRUCE_LOG); - this.playerBlocks.add(Material.BIRCH_LOG); - this.playerBlocks.add(Material.JUNGLE_LOG); - this.playerBlocks.add(Material.ACACIA_LOG); - this.playerBlocks.add(Material.DARK_OAK_LOG); - 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.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, 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.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.GRASS); - - ArrayList notSuitableForFillBlocks = new ArrayList<>(); - notSuitableForFillBlocks.add(Material.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 = EnumSet.noneOf(Material.class); - 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.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); - - //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; - } -} +/* + 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.EnumSet; +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"), + // Variants for versions < 1.18 + NamespacedKey.minecraft("modified_jungle"), + NamespacedKey.minecraft("jungle_hills"), + NamespacedKey.minecraft("bamboo_jungle_hills") + ); + + // 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"), + // Variants for versions < 1.18 + NamespacedKey.minecraft("desert_hills"), + NamespacedKey.minecraft("desert_lakes") + ); + + //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 = EnumSet.noneOf(Material.class); + 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 = EnumSet.noneOf(Material.class); + 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.add(Material.IRON_ORE); + this.playerBlocks.add(Material.GOLD_ORE); + this.playerBlocks.add(Material.DIAMOND_ORE); + this.playerBlocks.add(Material.MELON); + this.playerBlocks.add(Material.MELON_STEM); + this.playerBlocks.add(Material.BEDROCK); + this.playerBlocks.add(Material.COAL_ORE); + this.playerBlocks.add(Material.PUMPKIN); + this.playerBlocks.add(Material.PUMPKIN_STEM); + } + + if (this.aggressiveMode) + { + this.playerBlocks.add(Material.OAK_LEAVES); + this.playerBlocks.add(Material.SPRUCE_LEAVES); + this.playerBlocks.add(Material.BIRCH_LEAVES); + this.playerBlocks.add(Material.JUNGLE_LEAVES); + this.playerBlocks.add(Material.ACACIA_LEAVES); + this.playerBlocks.add(Material.DARK_OAK_LEAVES); + this.playerBlocks.add(Material.OAK_LOG); + this.playerBlocks.add(Material.SPRUCE_LOG); + this.playerBlocks.add(Material.BIRCH_LOG); + this.playerBlocks.add(Material.JUNGLE_LOG); + this.playerBlocks.add(Material.ACACIA_LOG); + this.playerBlocks.add(Material.DARK_OAK_LOG); + 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.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, 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 = EnumSet.noneOf(Material.class); + 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.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); + + //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; + } +}