From 446087ab48ed1ce2d9f3545a275dd4c48c86976e Mon Sep 17 00:00:00 2001 From: BONNe Date: Tue, 5 Mar 2019 11:24:34 +0200 Subject: [PATCH] Merge develop into master (#15) * Fixes BEETROOT_SEEDS enum for itemstack in schem https://github.com/BentoBoxWorld/CaveBlock/issues/12 * Adds error checking for MATERIALs in config.yml MATERIALs must be blocks otherwise the world populator will throw errors and crash the server. This commit checks that all imported materials are blocks and if they are not logs the error. Updated config.yml to use correct 1.13 material names for blocks. https://github.com/BentoBoxWorld/CaveBlock/issues/11 * Removes extraneous settings https://github.com/BentoBoxWorld/BentoBox/issues/531 * Implement ability to change user and admin command in config. * Improve CaveBlock pom.xml Remove unnecessary things. Add dependency projects in properties Add BuildNumber to version tag. * Add BentoBox 1.3.0 admin commands * Remove BeaconEnabler as it is not necessary. Beacon can shine through bedrock. Update pom as Challenges addon. --- .gitignore | 3 + pom.xml | 84 ++++---- .../world/bentobox/caveblock/CaveBlock.java | 2 - .../world/bentobox/caveblock/Settings.java | 136 +++++------- .../caveblock/commands/AdminCommand.java | 10 +- .../caveblock/commands/IslandCommand.java | 4 +- .../populators/MaterialPopulator.java | 6 +- .../caveblock/listeners/BeaconEnabler.java | 203 ------------------ src/main/resources/config.yml | 21 +- src/main/resources/schems/island.schem | Bin 2318 -> 2516 bytes 10 files changed, 127 insertions(+), 342 deletions(-) delete mode 100644 src/main/java/world/bentobox/caveblock/listeners/BeaconEnabler.java diff --git a/.gitignore b/.gitignore index c836fce..a581609 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* /target/ +/.DS_Store +/.classpath +/.project diff --git a/pom.xml b/pom.xml index c01cbfb..f583b0f 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ world.bentobox caveblock - 0.1.0 + ${revision} CaveBlock CaveBlock is an add-on for BentoBox, an expandable Minecraft Bukkit plugin for island-type games like SkyBlock or AcidIsland. @@ -44,9 +44,45 @@ UTF-8 UTF-8 1.8 - 1.7.4 + + 1.13.2-R0.1-SNAPSHOT + 1.3.0 + + ${build.version} + + 1.3.0 + + + develop + + + env.BUILD_NUMBER + + + + + ${build.version}-SNAPSHOT #${env.BUILD_NUMBER} + + + + + master + + + env.GIT_BRANCH + origin/master + + + + + ${build.version} + + + + + spigot-repo @@ -62,31 +98,13 @@ org.spigotmc spigot-api - 1.13.2-R0.1-SNAPSHOT + ${spigot.version} provided - - org.mockito - mockito-all - 1.10.19 - test - - - org.powermock - powermock-module-junit4 - ${powermock.version} - test - - - org.powermock - powermock-api-mockito - ${powermock.version} - test - world.bentobox bentobox - 1.2.0 + ${bentobox.version} provided @@ -199,28 +217,6 @@ maven-deploy-plugin 2.8.2 - - org.jacoco - jacoco-maven-plugin - 0.8.1 - - true - - - - pre-unit-test - - prepare-agent - - - - post-unit-test - - report - - - - \ No newline at end of file diff --git a/src/main/java/world/bentobox/caveblock/CaveBlock.java b/src/main/java/world/bentobox/caveblock/CaveBlock.java index c170de6..bc1acf7 100644 --- a/src/main/java/world/bentobox/caveblock/CaveBlock.java +++ b/src/main/java/world/bentobox/caveblock/CaveBlock.java @@ -13,7 +13,6 @@ import world.bentobox.bentobox.api.configuration.WorldSettings; import world.bentobox.caveblock.commands.AdminCommand; import world.bentobox.caveblock.commands.IslandCommand; import world.bentobox.caveblock.generators.ChunkGeneratorWorld; -import world.bentobox.caveblock.listeners.BeaconEnabler; import world.bentobox.caveblock.listeners.CustomHeightLimitations; @@ -165,7 +164,6 @@ public class CaveBlock extends GameModeAddon } this.getServer().getPluginManager().registerEvents(new CustomHeightLimitations(this), this.getPlugin()); - this.getServer().getPluginManager().registerEvents(new BeaconEnabler(this), this.getPlugin()); } diff --git a/src/main/java/world/bentobox/caveblock/Settings.java b/src/main/java/world/bentobox/caveblock/Settings.java index a1571bd..9f0e7c3 100644 --- a/src/main/java/world/bentobox/caveblock/Settings.java +++ b/src/main/java/world/bentobox/caveblock/Settings.java @@ -412,16 +412,6 @@ public class Settings implements DataObject, WorldSettings } - /** - * This method returns the resetConfirmation object. - * @return the resetConfirmation object. - */ - public boolean isResetConfirmation() - { - return resetConfirmation; - } - - /** * This method returns the leaversLoseReset object. * @return the leaversLoseReset object. @@ -508,16 +498,6 @@ public class Settings implements DataObject, WorldSettings } - /** - * This method returns the respawnOnIsland object. - * @return the respawnOnIsland object. - */ - public boolean isRespawnOnIsland() - { - return respawnOnIsland; - } - - /** * This method returns the allowSetHomeInNether object. * @return the allowSetHomeInNether object. @@ -627,16 +607,6 @@ public class Settings implements DataObject, WorldSettings } - /** - * This method returns the closePanelOnClickOutside object. - * @return the closePanelOnClickOutside object. - */ - public boolean isClosePanelOnClickOutside() - { - return closePanelOnClickOutside; - } - - /** * @return the permission prefix */ @@ -828,7 +798,27 @@ public class Settings implements DataObject, WorldSettings } - // --------------------------------------------------------------------- + /** + * This method returns the islandCommand value. + * @return the value of islandCommand. + */ + public String getIslandCommand() + { + return islandCommand; + } + + + /** + * This method returns the adminCommand value. + * @return the value of adminCommand. + */ + public String getAdminCommand() + { + return adminCommand; + } + + + // --------------------------------------------------------------------- // Section: Setters // --------------------------------------------------------------------- @@ -1195,17 +1185,6 @@ public class Settings implements DataObject, WorldSettings } - /** - * This method sets the resetConfirmation object value. - * @param resetConfirmation the resetConfirmation object new value. - * - */ - public void setResetConfirmation(boolean resetConfirmation) - { - this.resetConfirmation = resetConfirmation; - } - - /** * This method sets the leaversLoseReset object value. * @param leaversLoseReset the leaversLoseReset object new value. @@ -1294,17 +1273,6 @@ public class Settings implements DataObject, WorldSettings } - /** - * This method sets the respawnOnIsland object value. - * @param respawnOnIsland the respawnOnIsland object new value. - * - */ - public void setRespawnOnIsland(boolean respawnOnIsland) - { - this.respawnOnIsland = respawnOnIsland; - } - - /** * This method sets the allowSetHomeInNether object value. * @param allowSetHomeInNether the allowSetHomeInNether object new value. @@ -1414,18 +1382,6 @@ public class Settings implements DataObject, WorldSettings this.ivSettings = ivSettings; } - - /** - * This method sets the closePanelOnClickOutside object value. - * @param closePanelOnClickOutside the closePanelOnClickOutside object new value. - * - */ - public void setClosePanelOnClickOutside(boolean closePanelOnClickOutside) - { - this.closePanelOnClickOutside = closePanelOnClickOutside; - } - - /** * This method sets the resetEpoch object value. * @param resetEpoch the resetEpoch object new value. @@ -1629,10 +1585,45 @@ public class Settings implements DataObject, WorldSettings this.debug = debug; } - // --------------------------------------------------------------------- + + /** + * This method sets the islandCommand value. + * @param islandCommand the islandCommand new value. + * + */ + public void setIslandCommand(String islandCommand) + { + this.islandCommand = islandCommand; + } + + + /** + * This method sets the adminCommand value. + * @param adminCommand the adminCommand new value. + * + */ + public void setAdminCommand(String adminCommand) + { + this.adminCommand = adminCommand; + } + + + // --------------------------------------------------------------------- // Section: Variables // --------------------------------------------------------------------- + + /* Commands */ + @ConfigComment("Cave Command. What command users will run to access their cave.") + @ConfigComment("To define alias, just separate commands with white space.") + @ConfigEntry(path = "cave.command.island") + private String islandCommand = "cave cb"; + + @ConfigComment("The Cave admin command.") + @ConfigComment("To define alias, just separate commands with white space.") + @ConfigEntry(path = "cave.command.admin") + private String adminCommand = "cbadmin cba"; + /* WORLD */ @ConfigComment("Friendly name for this world. Used in admin commands. Must be a single word") @ConfigEntry(path = "world.friendly-name") @@ -1895,9 +1886,6 @@ public class Settings implements DataObject, WorldSettings @ConfigEntry(path = "island.reset.reset-limit") private int resetLimit = -1; - @ConfigEntry(path = "island.require-confirmation.reset") - private boolean resetConfirmation = true; - @ConfigComment("Kicked or leaving players lose resets") @ConfigComment("Players who leave a team will lose an island reset chance") @ConfigComment("If a player has zero resets left and leaves a team, they cannot make a new") @@ -1946,10 +1934,6 @@ public class Settings implements DataObject, WorldSettings @ConfigEntry(path = "island.reset.on-leave.ender-chest") private boolean onLeaveResetEnderChest = false; - @ConfigComment("Have player's respawn on their island if they die") - @ConfigEntry(path = "island.respawn-on-island") - private boolean respawnOnIsland = true; - // Sethome @ConfigEntry(path = "island.sethome.nether.allow") private boolean allowSetHomeInNether = true; @@ -1993,12 +1977,6 @@ public class Settings implements DataObject, WorldSettings @ConfigEntry(path = "protection.invincible-visitors") private List ivSettings = new ArrayList<>(); - //---------------------------------------------------------------------------------------/ - - @ConfigComment("Whether GUIs should be closed when the player clicks outside.") - @ConfigEntry(path = "panel.close-on-click-outside") - private boolean closePanelOnClickOutside = true; - //---------------------------------------------------------------------------------------/ @ConfigComment("These settings should not be edited") @ConfigEntry(path = "do-not-edit-these-settings.reset-epoch") diff --git a/src/main/java/world/bentobox/caveblock/commands/AdminCommand.java b/src/main/java/world/bentobox/caveblock/commands/AdminCommand.java index 30a8df1..4b88eee 100644 --- a/src/main/java/world/bentobox/caveblock/commands/AdminCommand.java +++ b/src/main/java/world/bentobox/caveblock/commands/AdminCommand.java @@ -21,7 +21,9 @@ import world.bentobox.caveblock.CaveBlock; public class AdminCommand extends CompositeCommand { public AdminCommand(CaveBlock addon) { - super(addon, "cbadmin", "cba"); + super(addon, + addon.getSettings().getAdminCommand().split(" ")[0], + addon.getSettings().getAdminCommand().split(" ")); } @Override @@ -62,6 +64,12 @@ public class AdminCommand extends CompositeCommand { new AdminReloadCommand(this); // Spawn new AdminSetspawnCommand(this); + // Reset flags + new AdminResetFlagsCommand(this); + // Trash + new AdminTrashCommand(this); + new AdminEmptyTrashCommand(this); + new AdminSwitchtoCommand(this); } @Override diff --git a/src/main/java/world/bentobox/caveblock/commands/IslandCommand.java b/src/main/java/world/bentobox/caveblock/commands/IslandCommand.java index 971dd77..df73586 100644 --- a/src/main/java/world/bentobox/caveblock/commands/IslandCommand.java +++ b/src/main/java/world/bentobox/caveblock/commands/IslandCommand.java @@ -15,7 +15,9 @@ import world.bentobox.caveblock.CaveBlock; public class IslandCommand extends CompositeCommand { public IslandCommand(CaveBlock addon) { - super(addon, "cave", "cb"); + super(addon, + addon.getSettings().getIslandCommand().split(" ")[0], + addon.getSettings().getIslandCommand().split(" ")); } /* (non-Javadoc) diff --git a/src/main/java/world/bentobox/caveblock/generators/populators/MaterialPopulator.java b/src/main/java/world/bentobox/caveblock/generators/populators/MaterialPopulator.java index c8bb8a5..5c3d0ff 100644 --- a/src/main/java/world/bentobox/caveblock/generators/populators/MaterialPopulator.java +++ b/src/main/java/world/bentobox/caveblock/generators/populators/MaterialPopulator.java @@ -146,11 +146,13 @@ public class MaterialPopulator extends BlockPopulator filter(splitString -> splitString.length == 4). forEach(splitString -> { Material material = Material.getMaterial(splitString[1]); - - if (material != null) + // Material must be a block otherwise the chunk cannot be populated + if (material != null && material.isBlock()) { materialMap.put(material, new Pair<>(Double.parseDouble(splitString[2]), Integer.parseInt(splitString[3]))); + } else { + addon.logError("Could not parse MATERIAL in config.yml: " + splitString[1] + " is not a valid block."); } }); diff --git a/src/main/java/world/bentobox/caveblock/listeners/BeaconEnabler.java b/src/main/java/world/bentobox/caveblock/listeners/BeaconEnabler.java deleted file mode 100644 index 61a8f4f..0000000 --- a/src/main/java/world/bentobox/caveblock/listeners/BeaconEnabler.java +++ /dev/null @@ -1,203 +0,0 @@ -package world.bentobox.caveblock.listeners; - - -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.block.BlockDamageEvent; -import org.bukkit.event.block.BlockExplodeEvent; -import org.bukkit.event.block.BlockPlaceEvent; -import org.bukkit.event.entity.EntityExplodeEvent; - -import world.bentobox.bentobox.util.Util; -import world.bentobox.caveblock.CaveBlock; -import world.bentobox.caveblock.Settings; - - -/** - * This class allows to enable beacon in CaveBlock, if cave roof is made of bedrock. - * It will replace Bedrock with black glass. - */ -public class BeaconEnabler implements Listener -{ - /** - * Constructor BeaconEnabler creates a new BeaconEnabler instance. - * - * @param addon of type CaveBlock - */ - public BeaconEnabler(CaveBlock addon) - { - this.addon = addon; - this.settings = addon.getSettings(); - } - - - /** - * Method onBlockPlacement detects if beacon is placed and replace roof bedrock with black glass. - * - * @param event of type BlockPlaceEvent - */ - @EventHandler(priority = EventPriority.LOWEST) - public void onBlockPlacement(BlockPlaceEvent event) - { - World world = event.getPlayer().getWorld(); - - if (!Util.sameWorld(this.addon.getOverWorld(), world) || - !this.settings.isBeaconAllowed() || - !this.isRoofEnabled(world) || - !event.getBlock().getType().equals(Material.BEACON)) - { - // This should work only if it is cave block world or world has roof from bedrock. Otherwise, - // players can dig till top themself. - return; - } - - - Block roofBlock = world.getBlockAt(event.getBlock().getX(), this.settings.getWorldDepth() - 1, event.getBlock().getZ()); - - if (roofBlock.getType().equals(Material.BEDROCK)) - { - // Replace only bedrock. - roofBlock.setType(Material.BLACK_STAINED_GLASS); - } - } - - - /** - * Method onBlockBreak detects if beacon is destroyed and replace roof black glass with bedrock. - * - * @param event of type BlockBreakEvent - */ - @EventHandler(priority = EventPriority.LOWEST) - public void onBlockBreak(BlockBreakEvent event) - { - World world = event.getPlayer().getWorld(); - - if (!Util.sameWorld(this.addon.getOverWorld(), world) || - !this.isRoofEnabled(world) || - !this.settings.isBeaconAllowed() || - !event.getBlock().getType().equals(Material.BEACON)) - { - // This should work only if it is cave block world or world has roof from bedrock. - return; - } - - Block roofBlock = world.getBlockAt(event.getBlock().getX(), this.settings.getWorldDepth() - 1, event.getBlock().getZ()); - - if (roofBlock.getType().equals(Material.BLACK_STAINED_GLASS)) - { - // Replace only black glass. - roofBlock.setType(Material.BEDROCK); - } - } - - - /** - * Method onBlockDamage detects if user tries to destroy black glass on roof and disable it. - * - * @param event of type BlockDamageEvent - */ - @EventHandler(priority = EventPriority.LOWEST) - public void onBlockDamage(BlockDamageEvent event) - { - World world = event.getPlayer().getWorld(); - - if (!Util.sameWorld(this.addon.getOverWorld(), world) || - !this.isRoofEnabled(world) || - !this.settings.isBeaconAllowed() || - event.getBlock().getY() != this.settings.getWorldDepth() - 1) - { - // This should work only if it is cave block world or world has roof from bedrock. - return; - } - - // Cancel break event if it is black glass. - event.setCancelled(event.getBlock().getType().equals(Material.BLACK_STAINED_GLASS)); - } - - - /** - * Method onBlockExplode detects if explosion tries to destroy black glass on roof and disable it. - * - * @param event of type BlockExplodeEvent - */ - @EventHandler(priority = EventPriority.LOWEST) - public void onBlockExplode(BlockExplodeEvent event) - { - World world = event.getBlock().getWorld(); - - if (!Util.sameWorld(this.addon.getOverWorld(), world) || - !this.isRoofEnabled(world) || - !this.settings.isBeaconAllowed() || - event.getBlock().getY() < this.settings.getWorldDepth() - 9) - { - // This should work only if it is cave block world or world has roof from bedrock. - return; - } - - final int blockY = this.settings.getWorldDepth() - 1; - - // Remove all black stained glass from explosion block list if it is on the roof. - event.blockList().removeIf(block -> - block.getY() == blockY && block.getType().equals(Material.BLACK_STAINED_GLASS)); - } - - - /** - * Method onEntityExplode detects if explosion tries to destroy black glass on roof and disable it. - * - * @param event of type EntityExplodeEvent - */ - @EventHandler(priority = EventPriority.LOWEST) - public void onEntityExplode(EntityExplodeEvent event) - { - World world = event.getLocation().getWorld(); - - if (!Util.sameWorld(this.addon.getOverWorld(), world) || - !this.isRoofEnabled(world) || - !this.settings.isBeaconAllowed() || - event.getLocation().getY() < this.settings.getWorldDepth() - 9) - { - // This should work only if it is cave block world or world has roof from bedrock. - return; - } - - final int blockY = this.settings.getWorldDepth() - 1; - - // Remove all black stained glass from explosion block list if it is on the roof. - event.blockList().removeIf(block -> - block.getY() == blockY && block.getType().equals(Material.BLACK_STAINED_GLASS)); - } - - - /** - * This method checks if in given world bedrock roof is enabled. - * @param world World that must be checked. - * @return true - bedrock roof is enabled, otherwise false - */ - private boolean isRoofEnabled(World world) - { - return world.getEnvironment().equals(World.Environment.NORMAL) && this.settings.isNormalRoof() || - world.getEnvironment().equals(World.Environment.NETHER) && this.settings.isNetherRoof() || - world.getEnvironment().equals(World.Environment.THE_END) && this.settings.isEndRoof(); - } - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - /** - * CaveBlock addon. - */ - private CaveBlock addon; - - /** - * Addon settings. - */ - private Settings settings; -} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index dd5a831..a6fb763 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -2,6 +2,14 @@ # This config file is dynamic and saved when the server is shutdown. # If you edit it while the server is running use /cbadmin reload # otherwise your settings will be lost. +caveblock: + command: + # Cave Command. What command users will run to access their cave. + # To define alias, just separate commands with white space. + island: cave cb + # The Cave admin command. + # To define alias, just separate commands with white space. + admin: cbadmin cba world: # Friendly name for this world. Used in admin commands. Must be a single word friendly-name: CaveBlock @@ -133,11 +141,11 @@ world: # MATERIAL:DIAMOND_ORE:100:5 - means there is 100% chace of spawing diamonds # where max amount in pack are 5 per each subchunk! blocks: - - MATERIAL:QUARTZ_ORE:30:5 + - MATERIAL:NETHER_QUARTZ_ORE:30:5 - MATERIAL:SOUL_SAND:40:10 - MATERIAL:MAGMA_BLOCK:10:3 - MATERIAL:GLOWSTONE:20:8 - - MATERIAL:NETHER_BRICK:10:5 + - MATERIAL:NETHER_BRICKS:10:5 - MATERIAL:LAVA:10:1 - ENTITY:MAGMA_CUBE:0.5:1 - ENTITY:GHAST:0.1:1 @@ -166,7 +174,7 @@ world: blocks: - ENTITY:SHULKER:0.2:1 - MATERIAL:OBSIDIAN:1:1 - - MATERIAL:CHORUS_FRUIT:1:3 + - MATERIAL:CHORUS_PLANT:1:3 # Mob white list - these mobs will NOT be removed when logging in or doing /cave remove-mobs-whitelist: - WITHER @@ -311,10 +319,6 @@ island: inventory: false # Reset Ender Chest - if true, the player's Ender Chest will be cleared. ender-chest: false - require-confirmation: - reset: true - # Have player's respawn on their island if they die - respawn-on-island: false sethome: nether: allow: true @@ -364,9 +368,6 @@ protection: - HOT_FLOOR - CRAMMING - VOID -panel: - # Whether GUIs should be closed when the player clicks outside. - close-on-click-outside: true do-not-edit-these-settings: # These settings should not be edited reset-epoch: 0 diff --git a/src/main/resources/schems/island.schem b/src/main/resources/schems/island.schem index 8d861e2ced35b66bbb44503bb8ac58ed0a118ab1..aa706fcb19a0f73ded592133706d5a2aea03b316 100644 GIT binary patch literal 2516 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 W=1kuS4gh%EkI4?;`7!GO0R9UOj7oR_