diff --git a/src/main/java/com/github/devcyntrix/deathchest/Constants.java b/src/main/java/com/github/devcyntrix/deathchest/Constants.java new file mode 100644 index 0000000..4135e24 --- /dev/null +++ b/src/main/java/com/github/devcyntrix/deathchest/Constants.java @@ -0,0 +1,9 @@ +package com.github.devcyntrix.deathchest; + +import java.text.DateFormat; + +public interface Constants { + + int DATE_FORMAT_CONFIG = DateFormat.DEFAULT; + +} diff --git a/src/main/java/com/github/devcyntrix/deathchest/DeathChestPlugin.java b/src/main/java/com/github/devcyntrix/deathchest/DeathChestPlugin.java index 4589647..39bb005 100644 --- a/src/main/java/com/github/devcyntrix/deathchest/DeathChestPlugin.java +++ b/src/main/java/com/github/devcyntrix/deathchest/DeathChestPlugin.java @@ -23,6 +23,7 @@ import com.github.devcyntrix.deathchest.support.protection.WorldGuardDeathChestFlag; import com.github.devcyntrix.deathchest.support.storage.MemoryStorage; import com.github.devcyntrix.deathchest.support.storage.YamlStorage; +import com.github.devcyntrix.deathchest.util.LocationUtil; import com.github.devcyntrix.deathchest.util.adapter.DurationAdapter; import com.github.devcyntrix.deathchest.view.chest.*; import com.github.devcyntrix.deathchest.view.update.AdminJoinNotificationView; @@ -58,7 +59,7 @@ import java.util.logging.Level; import java.util.stream.Stream; -import static com.github.devcyntrix.deathchest.api.report.ReportManager.DATE_FORMAT_CONFIG; +import static com.github.devcyntrix.deathchest.Constants.DATE_FORMAT_CONFIG; /** * This plugin creates chests if a player dies and will destroy them after a specific time. @@ -200,8 +201,7 @@ public void onDisable() { this.compatibilityManager.disableCompatibilities(); } - if (this.audiences != null) - this.audiences.close(); + if (this.audiences != null) this.audiences.close(); } @Override @@ -222,22 +222,21 @@ public void onLoad() { @Override public void onEnable() { debug(0, "Loading configuration file..."); - if (!isTest()) - reloadConfig(); + if (!isTest()) reloadConfig(); - initializeServices(); + loadServices(); debug(0, "Registering commands..."); CommandRegistry.create(this).registerCommands(this); - if (!test) { + if (!isTest()) { debug(0, "Starting metrics..."); new Metrics(this, BSTATS_ID); } } - private void initializeServices() { + private void loadServices() { placeholderAPIEnabled = Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI"); this.audiences = BukkitAudiences.create(this); @@ -283,7 +282,7 @@ private void initializeServices() { this.placeHolderController = new PlaceholderController(getDeathChestConfig()); debug(0, "Using death chest yaml storage"); - if (!test) { + if (!isTest()) { this.deathChestStorage = new YamlStorage(this.placeHolderController); } else { this.deathChestStorage = new MemoryStorage(); @@ -399,7 +398,7 @@ public void saveChests() { public void reload() { onDisable(); reloadConfig(); - initializeServices(); + loadServices(); } @Override @@ -411,11 +410,7 @@ public void reloadConfig() { debug(1, "Parsing configuration file..."); this.deathChestConfig = DeathChestConfig.load(getConfig()); if (isDebugMode()) { - Gson gson = new GsonBuilder() - .setPrettyPrinting() - .setDateFormat(DATE_FORMAT_CONFIG, DATE_FORMAT_CONFIG) - .registerTypeAdapter(Duration.class, new DurationAdapter()) - .create(); + Gson gson = new GsonBuilder().setPrettyPrinting().setDateFormat(DATE_FORMAT_CONFIG, DATE_FORMAT_CONFIG).registerTypeAdapter(Duration.class, new DurationAdapter()).create(); debug(1, "Configuration: " + gson.toJson(this.deathChestConfig)); } } @@ -428,15 +423,10 @@ public void reloadConfig() { */ @Override public boolean canPlaceChestAt(@NotNull Location location) { - World world = location.getWorld(); - if (world == null) - return false; - if (location.getY() < world.getMinHeight()) - return false; - if (location.getY() >= world.getMaxHeight()) - return false; - - return deathChestController.getChest(location) == null && !location.getBlock().getType().isSolid() && location.getBlock().getType() != Material.NETHER_PORTAL; + return LocationUtil.isValidBlock(location) && + deathChestController.getChest(location) == null && + !location.getBlock().getType().isSolid() && + location.getBlock().getType() != Material.NETHER_PORTAL; // To avoid a nether portal bug } @Override diff --git a/src/main/java/com/github/devcyntrix/deathchest/api/report/ReportManager.java b/src/main/java/com/github/devcyntrix/deathchest/api/report/ReportManager.java index 75be40a..808fc0e 100644 --- a/src/main/java/com/github/devcyntrix/deathchest/api/report/ReportManager.java +++ b/src/main/java/com/github/devcyntrix/deathchest/api/report/ReportManager.java @@ -4,7 +4,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.text.DateFormat; import java.text.ParseException; import java.text.ParsePosition; import java.util.Date; @@ -13,9 +12,6 @@ public interface ReportManager { - - int DATE_FORMAT_CONFIG = DateFormat.DEFAULT; - default void createReport() { addReport(Report.create()); } diff --git a/src/main/java/com/github/devcyntrix/deathchest/config/DeathChestConfig.java b/src/main/java/com/github/devcyntrix/deathchest/config/DeathChestConfig.java index fa785b6..68c7dfa 100644 --- a/src/main/java/com/github/devcyntrix/deathchest/config/DeathChestConfig.java +++ b/src/main/java/com/github/devcyntrix/deathchest/config/DeathChestConfig.java @@ -22,6 +22,7 @@ public record DeathChestConfig( @SerializedName("world-filter") @NotNull WorldFilterConfig worldFilterConfig, @SerializedName("world-chest-protection-filter") @NotNull WorldFilterConfig worldChestProtectionFilter, @SerializedName("world-alias") @NotNull WorldAliasConfig worldAlias, + @SerializedName("drop-conditions") @NotNull DropConditionConfig dropCondition, @SerializedName("preferred-animation-service") @Nullable String preferredBlockBreakAnimationService) { public static final int CONFIG_VERSION = 3; @@ -57,9 +58,11 @@ public static DeathChestConfig load(FileConfiguration config) { WorldFilterConfig worldChestProtectionFilterConfig = WorldFilterConfig.load(config.getConfigurationSection("world-chest-protection-filter")); WorldAliasConfig worldAliasConfig = WorldAliasConfig.load(config.getConfigurationSection("world-alias")); + DropConditionConfig dropConditions = DropConditionConfig.load(config.getConfigurationSection("drop-conditions")); + String preferredAnimationService = config.getString("preferred-animation-service"); - return new DeathChestConfig(configVersion, debug, updateCheck, autoUpdate, durationFormat, chestOptions, inventoryOptions, hologramOptions, particleOptions, breakAnimationOptions, playerNotificationOptions, globalNotificationOptions, changeDeathMessageOptions, worldFilterConfig, worldChestProtectionFilterConfig, worldAliasConfig, preferredAnimationService); + return new DeathChestConfig(configVersion, debug, updateCheck, autoUpdate, durationFormat, chestOptions, inventoryOptions, hologramOptions, particleOptions, breakAnimationOptions, playerNotificationOptions, globalNotificationOptions, changeDeathMessageOptions, worldFilterConfig, worldChestProtectionFilterConfig, worldAliasConfig, dropConditions, preferredAnimationService); } } diff --git a/src/main/java/com/github/devcyntrix/deathchest/config/DropCondition.java b/src/main/java/com/github/devcyntrix/deathchest/config/DropCondition.java new file mode 100644 index 0000000..b0ecffb --- /dev/null +++ b/src/main/java/com/github/devcyntrix/deathchest/config/DropCondition.java @@ -0,0 +1,10 @@ +package com.github.devcyntrix.deathchest.config; + +import org.bukkit.Location; +import org.jetbrains.annotations.NotNull; + +public interface DropCondition { + + boolean shouldDropItems(@NotNull Location location); + +} diff --git a/src/main/java/com/github/devcyntrix/deathchest/config/DropConditionConfig.java b/src/main/java/com/github/devcyntrix/deathchest/config/DropConditionConfig.java new file mode 100644 index 0000000..daddc48 --- /dev/null +++ b/src/main/java/com/github/devcyntrix/deathchest/config/DropConditionConfig.java @@ -0,0 +1,60 @@ +package com.github.devcyntrix.deathchest.config; + +import com.github.devcyntrix.deathchest.util.LocationUtil; +import com.google.gson.annotations.SerializedName; +import lombok.Getter; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.MemoryConfiguration; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashSet; +import java.util.Set; + +public class DropConditionConfig { + + @SerializedName("void-death") + private boolean voidDeath; + @SerializedName("lava-death") + private boolean lavaDeath; + @SerializedName("fire-death") + private boolean fireDeath; + + @Getter + private final transient Set appliedConditions = new HashSet<>(); + + public DropConditionConfig(boolean voidDeath, boolean lavaDeath, boolean fireDeath) { + this.voidDeath = voidDeath; + this.lavaDeath = lavaDeath; + this.fireDeath = fireDeath; + + if (voidDeath) appliedConditions.add(location -> { + return location.getBlockY() < location.getWorld().getMinHeight(); + }); + if (lavaDeath) appliedConditions.add(location -> { + return LocationUtil.isValidBlock(location) && location.getBlock().getType() == Material.LAVA; + }); + if (fireDeath) appliedConditions.add(location -> { + return LocationUtil.isValidBlock(location) && location.getBlock().getType() == Material.FIRE; + }); + } + + public boolean shouldDrop(Location location) { + return appliedConditions.stream().anyMatch(dropCondition -> dropCondition.shouldDropItems(location)); + } + + @Contract("null -> new") + public static @NotNull DropConditionConfig load(@Nullable ConfigurationSection section) { + if (section == null) section = new MemoryConfiguration(); + + boolean voidDeath = section.getBoolean("void-death", false); + boolean lavaDeath = section.getBoolean("lava-death", false); + boolean fireDeath = section.getBoolean("fire-death", false); + + return new DropConditionConfig(voidDeath, lavaDeath, fireDeath); + } + +} diff --git a/src/main/java/com/github/devcyntrix/deathchest/controller/LastSafeLocationController.java b/src/main/java/com/github/devcyntrix/deathchest/controller/LastSafeLocationController.java index 5976efc..a4302ff 100644 --- a/src/main/java/com/github/devcyntrix/deathchest/controller/LastSafeLocationController.java +++ b/src/main/java/com/github/devcyntrix/deathchest/controller/LastSafeLocationController.java @@ -2,6 +2,7 @@ import com.github.devcyntrix.deathchest.DeathChestPlugin; import com.github.devcyntrix.deathchest.util.LastLocationMetadata; +import com.github.devcyntrix.deathchest.util.LocationUtil; import com.google.inject.Singleton; import org.bukkit.Location; import org.bukkit.block.Block; @@ -24,6 +25,10 @@ public LastSafeLocationController(DeathChestPlugin plugin) { public void updatePosition(Player player) { Location location = player.getLocation().clone().subtract(0, 0.2, 0); + + if (!LocationUtil.isValidBlock(location)) + return; + Block block = location.getBlock(); if (block.isEmpty()) // Check if the player is in air return; diff --git a/src/main/java/com/github/devcyntrix/deathchest/listener/SpawnChestListener.java b/src/main/java/com/github/devcyntrix/deathchest/listener/SpawnChestListener.java index eb47020..31d4b5c 100644 --- a/src/main/java/com/github/devcyntrix/deathchest/listener/SpawnChestListener.java +++ b/src/main/java/com/github/devcyntrix/deathchest/listener/SpawnChestListener.java @@ -100,6 +100,9 @@ public void onDeath(PlayerDeathEvent event) { if (!config.worldFilterConfig().test(player.getWorld())) return; + if (config.dropCondition().shouldDrop(player.getLocation())) + return; + plugin.debug(1, "Getting expiration time..."); Duration expiration = config.chestOptions().expiration(); if (expiration == null) @@ -160,7 +163,7 @@ public void onDeath(PlayerDeathEvent event) { Location lastSafePos = controller.getPosition(player); if (lastSafePos == null) { - lastSafePos = player.getLocation().getBlock().getLocation().clone(); + lastSafePos = player.getLocation().clone(); } else if (player.getLocation().distanceSquared(lastSafePos) >= 20 * 20) { // Spawn the chest near to the player death location if the safe position distance is higher than 20 Blocks lastSafePos = player.getLocation().getBlock().getLocation().clone(); diff --git a/src/main/java/com/github/devcyntrix/deathchest/listener/WorldListener.java b/src/main/java/com/github/devcyntrix/deathchest/listener/WorldListener.java index 365ea8c..1b58e22 100644 --- a/src/main/java/com/github/devcyntrix/deathchest/listener/WorldListener.java +++ b/src/main/java/com/github/devcyntrix/deathchest/listener/WorldListener.java @@ -34,8 +34,8 @@ public void onWorldUnload(WorldUnloadEvent event) { // if (!event.isSneaking()) // return; // -// String json = "[{\"v\":3700,\"type\":\"FLINT_AND_STEEL\",\"meta\":{\"meta-type\":\"UNSPECIFIC\",\"Damage\":32}},{\"v\":3700,\"type\":\"COOKED_BEEF\",\"amount\":15},{\"v\":3700,\"type\":\"COOKED_MUTTON\"},{\"v\":3700,\"type\":\"NETHERITE_SWORD\",\"meta\":{\"meta-type\":\"UNSPECIFIC\",\"enchants\":{\"FIRE_ASPECT\":2,\"KNOCKBACK\":2,\"LOOT_BONUS_MOBS\":4,\"MENDING\":1,\"DAMAGE_ALL\":5,\"SWEEPING_EDGE\":3,\"DURABILITY\":5}}},{\"v\":3700,\"type\":\"DIAMOND_AXE\",\"meta\":{\"meta-type\":\"UNSPECIFIC\",\"Damage\":1034}},{\"v\":3700,\"type\":\"ENCHANTED_BOOK\",\"meta\":{\"meta-type\":\"ENCHANTED\",\"stored-enchants\":{\"SOUL_SPEED\":2}}},{\"v\":3700,\"type\":\"NETHERITE_PICKAXE\",\"meta\":{\"meta-type\":\"UNSPECIFIC\",\"enchants\":{\"DIG_SPEED\":5,\"LOOT_BONUS_BLOCKS\":4,\"MENDING\":1,\"DURABILITY\":4},\"Damage\":18}},{\"v\":3700,\"type\":\"ENDER_PEARL\",\"amount\":15},{\"v\":3700,\"type\":\"CHISELED_POLISHED_BLACKSTONE\",\"amount\":2},{\"v\":3700,\"type\":\"SAND\",\"amount\":44},{\"v\":3700,\"type\":\"SAND\",\"amount\":8},{\"v\":3700,\"type\":\"REDSTONE\"},{\"v\":3700,\"type\":\"DIAMOND_BOOTS\"},{\"v\":3700,\"type\":\"DIAMOND_SHOVEL\",\"meta\":{\"meta-type\":\"UNSPECIFIC\",\"enchants\":{\"DIG_SPEED\":3,\"DURABILITY\":3},\"Damage\":53}},{\"v\":3700,\"type\":\"WHITE_WOOL\",\"amount\":16}]"; -// Gson gson = new GsonBuilder() +// String json = "[{\"v\":3465,\"type\":\"NETHERITE_SWORD\",\"meta\":{\"meta-type\":\"UNSPECIFIC\",\"display-name\":\"{\\\"extra\\\":[{\\\"bold\\\":true,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":true,\\\"color\\\":\\\"red\\\",\\\"text\\\":\\\"x\\\"},{\\\"bold\\\":false,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":false,\\\"color\\\":\\\"gold\\\",\\\"text\\\":\\\"Prestige Sword\\\"},{\\\"bold\\\":true,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":true,\\\"color\\\":\\\"red\\\",\\\"text\\\":\\\"x\\\"}],\\\"text\\\":\\\"\\\"}\",\"lore\":[\"{\\\"extra\\\":[{\\\"bold\\\":false,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":false,\\\"color\\\":\\\"#EAD931\\\",\\\"text\\\":\\\"Vampire I\\\"}],\\\"text\\\":\\\"\\\"}\",\"{\\\"extra\\\":[{\\\"bold\\\":false,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":false,\\\"color\\\":\\\"#EA9631\\\",\\\"text\\\":\\\"Double Strike I\\\"}],\\\"text\\\":\\\"\\\"}\",\"{\\\"extra\\\":[{\\\"bold\\\":false,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":false,\\\"color\\\":\\\"#74EA31\\\",\\\"text\\\":\\\"Exp Hunter I\\\"}],\\\"text\\\":\\\"\\\"}\",\"{\\\"extra\\\":[{\\\"bold\\\":false,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":false,\\\"color\\\":\\\"dark_gray\\\",\\\"text\\\":\\\"▸ 30% chance to heal for 0.25 heart(s) on hit.\\\"}],\\\"text\\\":\\\"\\\"}\",\"{\\\"extra\\\":[{\\\"bold\\\":false,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":false,\\\"color\\\":\\\"dark_gray\\\",\\\"text\\\":\\\"▸ 4.8% chance to inflict double damage.\\\"}],\\\"text\\\":\\\"\\\"}\",\"{\\\"extra\\\":[{\\\"bold\\\":false,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":false,\\\"color\\\":\\\"dark_gray\\\",\\\"text\\\":\\\"▸ Increases exp drop from mobs by 50%.\\\"}],\\\"text\\\":\\\"\\\"}\"],\"enchants\":{\"DAMAGE_ALL\":7,\"DURABILITY\":7,\"FIRE_ASPECT\":5,\"LOOT_BONUS_MOBS\":7,\"KNOCKBACK\":2,\"EXP_HUNTER\":1,\"DOUBLE_STRIKE\":1,\"VAMPIRE\":1},\"Damage\":75,\"PublicBukkitValues\":{\"excellentenchants:lore_size\":\"6i\"}}},{\"v\":3465,\"type\":\"DIAMOND_AXE\",\"meta\":{\"meta-type\":\"UNSPECIFIC\",\"display-name\":\"{\\\"extra\\\":[{\\\"bold\\\":false,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":false,\\\"color\\\":\\\"red\\\",\\\"text\\\":\\\"Elite Tools\\\"}],\\\"text\\\":\\\"\\\"}\",\"lore\":[\"{\\\"extra\\\":[{\\\"bold\\\":false,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":false,\\\"color\\\":\\\"#EAD931\\\",\\\"text\\\":\\\"Telekinesis\\\"}],\\\"text\\\":\\\"\\\"}\",\"{\\\"extra\\\":[{\\\"bold\\\":false,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":false,\\\"color\\\":\\\"dark_gray\\\",\\\"text\\\":\\\"▸ Moves all blocks loot directly to your inventory.\\\"}],\\\"text\\\":\\\"\\\"}\"],\"enchants\":{\"DIG_SPEED\":5,\"DURABILITY\":5,\"SILK_TOUCH\":1,\"TELEKINESIS\":1},\"repair-cost\":1,\"Damage\":348,\"PublicBukkitValues\":{\"excellentenchants:lore_size\":\"2i\"}}},{\"v\":3465,\"type\":\"NETHERITE_PICKAXE\",\"meta\":{\"meta-type\":\"UNSPECIFIC\",\"display-name\":\"{\\\"extra\\\":[{\\\"bold\\\":false,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":true,\\\"color\\\":\\\"aqua\\\",\\\"text\\\":\\\"l\\\"},{\\\"bold\\\":true,\\\"italic\\\":true,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":false,\\\"color\\\":\\\"yellow\\\",\\\"text\\\":\\\"Giga-Drill\\\"},{\\\"bold\\\":false,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":true,\\\"color\\\":\\\"aqua\\\",\\\"text\\\":\\\"l\\\"}],\\\"text\\\":\\\"\\\"}\",\"lore\":[\"{\\\"extra\\\":[{\\\"bold\\\":false,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":false,\\\"color\\\":\\\"#74EA31\\\",\\\"text\\\":\\\"Smelter III\\\"}],\\\"text\\\":\\\"\\\"}\",\"{\\\"extra\\\":[{\\\"bold\\\":false,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":false,\\\"color\\\":\\\"dark_gray\\\",\\\"text\\\":\\\"▸ 30% chance to smelt a block/ore.\\\"}],\\\"text\\\":\\\"\\\"}\"],\"enchants\":{\"DURABILITY\":10,\"LOOT_BONUS_BLOCKS\":5,\"MENDING\":1,\"DIG_SPEED\":10,\"SMELTER\":3},\"repair-cost\":1,\"Damage\":3,\"PublicBukkitValues\":{\"excellentenchants:lore_size\":\"2i\"}}},{\"v\":3465,\"type\":\"DIAMOND_SHOVEL\",\"meta\":{\"meta-type\":\"UNSPECIFIC\",\"display-name\":\"{\\\"extra\\\":[{\\\"bold\\\":false,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":false,\\\"color\\\":\\\"red\\\",\\\"text\\\":\\\"Elite Tools\\\"}],\\\"text\\\":\\\"\\\"}\",\"enchants\":{\"DIG_SPEED\":5,\"DURABILITY\":5},\"Damage\":535}},{\"v\":3465,\"type\":\"BOW\",\"meta\":{\"meta-type\":\"UNSPECIFIC\",\"Damage\":274}},{\"v\":3465,\"type\":\"DIAMOND_PICKAXE\",\"meta\":{\"meta-type\":\"UNSPECIFIC\",\"display-name\":\"{\\\"extra\\\":[{\\\"bold\\\":true,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":false,\\\"color\\\":\\\"light_purple\\\",\\\"text\\\":\\\"Veinminer Pick\\\"}],\\\"text\\\":\\\"\\\"}\",\"lore\":[\"{\\\"extra\\\":[{\\\"bold\\\":false,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":false,\\\"color\\\":\\\"#74EA31\\\",\\\"text\\\":\\\"Veinminer I\\\"}],\\\"text\\\":\\\"\\\"}\",\"{\\\"extra\\\":[{\\\"bold\\\":false,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":false,\\\"color\\\":\\\"dark_gray\\\",\\\"text\\\":\\\"▸ Mines up to 7 blocks of the ore vein at once.\\\"}],\\\"text\\\":\\\"\\\"}\"],\"enchants\":{\"VEINMINER\":1},\"Damage\":3,\"PublicBukkitValues\":{\"excellentenchants:lore_size\":\"2i\"}}},{\"v\":3465,\"type\":\"ARROW\",\"amount\":27},{\"v\":3465,\"type\":\"COOKED_BEEF\",\"amount\":43},{\"v\":3465,\"type\":\"BONE\",\"amount\":25},{\"v\":3465,\"type\":\"DIAMOND_PICKAXE\",\"meta\":{\"meta-type\":\"UNSPECIFIC\",\"enchants\":{\"DURABILITY\":3,\"DIG_SPEED\":4,\"SILK_TOUCH\":1},\"repair-cost\":1,\"Damage\":131}},{\"v\":3465,\"type\":\"LEATHER_LEGGINGS\",\"meta\":{\"meta-type\":\"COLORABLE_ARMOR\",\"display-name\":\"{\\\"extra\\\":[{\\\"bold\\\":true,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":false,\\\"color\\\":\\\"light_purple\\\",\\\"text\\\":\\\"Traveler\\u0027s Trousers\\\"}],\\\"text\\\":\\\"\\\"}\",\"lore\":[\"{\\\"extra\\\":[{\\\"bold\\\":false,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":false,\\\"color\\\":\\\"yellow\\\",\\\"text\\\":\\\"For when you need to get\\\"}],\\\"text\\\":\\\"\\\"}\",\"{\\\"extra\\\":[{\\\"bold\\\":false,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":false,\\\"color\\\":\\\"yellow\\\",\\\"text\\\":\\\"somewhere quick...\\\"}],\\\"text\\\":\\\"\\\"}\"],\"enchants\":{\"PROTECTION_FALL\":5,\"DURABILITY\":5,\"SWIFT_SNEAK\":3,\"DEPTH_STRIDER\":3,\"MENDING\":1},\"repair-cost\":1,\"Damage\":1,\"internal\":\"H4sIAAAAAAAA/+NiYOBi4PB0jfdwjfBx5WBgL8gvyczPM2TgLC5ITU2xs7MzYmAAABLysLYlAAAA\"}},{\"v\":3465,\"type\":\"ELYTRA\",\"meta\":{\"meta-type\":\"UNSPECIFIC\",\"display-name\":\"{\\\"extra\\\":[{\\\"bold\\\":false,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":false,\\\"color\\\":\\\"light_purple\\\",\\\"text\\\":\\\"Ashley\\u0027s Wings\\\"}],\\\"text\\\":\\\"\\\"}\",\"enchants\":{\"THORNS\":3,\"DURABILITY\":5,\"MENDING\":1},\"repair-cost\":1}},{\"v\":3465,\"type\":\"TURTLE_HELMET\",\"meta\":{\"meta-type\":\"ARMOR\",\"display-name\":\"{\\\"extra\\\":[{\\\"bold\\\":false,\\\"italic\\\":false,\\\"underlined\\\":false,\\\"strikethrough\\\":false,\\\"obfuscated\\\":false,\\\"color\\\":\\\"light_purple\\\",\\\"text\\\":\\\"tinybirb\\u0027s Turtle Shell\\\"}],\\\"text\\\":\\\"\\\"}\",\"enchants\":{\"WATER_WORKER\":5,\"OXYGEN\":5,\"PROTECTION_ENVIRONMENTAL\":5,\"DURABILITY\":3,\"MENDING\":1,\"THORNS\":3},\"repair-cost\":15,\"Damage\":1}},{\"v\":3465,\"type\":\"SHIELD\",\"meta\":{\"meta-type\":\"TILE_ENTITY\",\"enchants\":{\"MENDING\":1},\"repair-cost\":1,\"blockMaterial\":\"SHIELD\"}}]"; +// Gson gson = new GsonBuilder() // .registerTypeHierarchyAdapter(ItemStack.class, new ItemStackAdapter()) // .registerTypeHierarchyAdapter(ItemMeta.class, new ItemMetaAdapter()) // .create(); diff --git a/src/main/java/com/github/devcyntrix/deathchest/report/GsonReportManager.java b/src/main/java/com/github/devcyntrix/deathchest/report/GsonReportManager.java index 2a561cd..a050580 100644 --- a/src/main/java/com/github/devcyntrix/deathchest/report/GsonReportManager.java +++ b/src/main/java/com/github/devcyntrix/deathchest/report/GsonReportManager.java @@ -23,6 +23,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import static com.github.devcyntrix.deathchest.Constants.DATE_FORMAT_CONFIG; + public class GsonReportManager implements ReportManager { private static final Logger LOG = Logger.getLogger(GsonReportManager.class.getName()); diff --git a/src/main/java/com/github/devcyntrix/deathchest/util/LocationUtil.java b/src/main/java/com/github/devcyntrix/deathchest/util/LocationUtil.java new file mode 100644 index 0000000..3692390 --- /dev/null +++ b/src/main/java/com/github/devcyntrix/deathchest/util/LocationUtil.java @@ -0,0 +1,13 @@ +package com.github.devcyntrix.deathchest.util; + +import org.bukkit.Location; + +public final class LocationUtil { + + public static boolean isValidBlock(Location location) { + if (location.getWorld() == null) + return false; + return location.getBlockY() >= location.getWorld().getMinHeight() && location.getBlockY() < location.getWorld().getMaxHeight(); + } + +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 6f2ce5d..109bd89 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -140,6 +140,12 @@ world-alias: world_nether: "Nether" world_the_end: "The End" +# Drops the items if any of the conditions becomes the true when activated. +drop-conditions: + lava-death: false + fire-death: false + void-death: false + # Do not touch this! config-version: 3 # Prints debug messages if property is set to true diff --git a/src/test/java/com/github/devcyntrix/deathchest/ChestSpawnTest.java b/src/test/java/com/github/devcyntrix/deathchest/ChestSpawnTest.java index afa0c3d..6cd9ca0 100644 --- a/src/test/java/com/github/devcyntrix/deathchest/ChestSpawnTest.java +++ b/src/test/java/com/github/devcyntrix/deathchest/ChestSpawnTest.java @@ -40,6 +40,7 @@ public void setUp() { } this.server = MockBukkit.getOrCreateMock(); + Assertions.assertNotNull(this.server); this.server.setSpawnRadius(0); MockBukkit.load(DeathChestPlugin.class, true, config); } @@ -54,33 +55,46 @@ public void tearDown() { public void doesntSpawnEmptyChest() { PlayerMock player = server.addPlayer(); @NotNull Location location = player.getLocation(); + + // Kills the player player.setHealth(0.0); - Assertions.assertTrue(location.getBlock().isEmpty()); + // Wait one tick that the chest can spawn + server.getScheduler().performOneTick(); + + Assertions.assertEquals(Material.AIR, location.getBlock().getType()); } + /** + * Checks the spawn of a chest if a player dies and that the items drops in the world if the player breaks + * the chest. + */ @Test - @DisplayName("Spawn filled chest") + @DisplayName("Spawn filled chest and drop items") public void spawnFilledChest() { List list = new ArrayList<>(List.of(new ItemStack(Material.OAK_LOG))); PlayerMock player = server.addPlayer(); + @NotNull Location location = player.getLocation(); + + // Give items PlayerInventory inventory = player.getInventory(); - System.out.println("Adding item to the inventory..."); inventory.addItem(list.toArray(ItemStack[]::new)); - @NotNull Location location = player.getLocation(); - System.out.println("Killing player..."); + // Kills the player player.setHealth(0.0); + // Wait one tick that the chest can spawn server.getScheduler().performOneTick(); + Block block = location.getBlock(); - Assertions.assertFalse(block.isEmpty()); - System.out.println("Breaking block..."); + Assertions.assertEquals(Material.CHEST, block.getType()); + // Simulate the block break BlockBreakEvent blockBreakEvent = player.simulateBlockBreak(block); Assertions.assertNotNull(blockBreakEvent); Assertions.assertTrue(blockBreakEvent.isCancelled()); - Assertions.assertTrue(block.isEmpty()); + Assertions.assertEquals(Material.AIR, block.getType()); + // Check that the items drops WorldMock world = player.getWorld(); Collection items = world.getEntitiesByClass(Item.class); items.forEach(item -> list.remove(item.getItemStack())); diff --git a/src/test/java/com/github/devcyntrix/deathchest/dropconditions/FireDropConditionCheck.java b/src/test/java/com/github/devcyntrix/deathchest/dropconditions/FireDropConditionCheck.java new file mode 100644 index 0000000..b2f96e9 --- /dev/null +++ b/src/test/java/com/github/devcyntrix/deathchest/dropconditions/FireDropConditionCheck.java @@ -0,0 +1,149 @@ +package com.github.devcyntrix.deathchest.dropconditions; + +import be.seeseemelk.mockbukkit.MockBukkit; +import be.seeseemelk.mockbukkit.ServerMock; +import be.seeseemelk.mockbukkit.WorldMock; +import be.seeseemelk.mockbukkit.entity.PlayerMock; +import com.github.devcyntrix.deathchest.DeathChestModel; +import com.github.devcyntrix.deathchest.DeathChestPlugin; +import com.github.devcyntrix.deathchest.config.DeathChestConfig; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.junit.jupiter.api.*; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +@DisplayName("Fire drop condition check") +public class FireDropConditionCheck { + + private ServerMock server; + + @BeforeEach + public void setUp() { + this.server = MockBukkit.getOrCreateMock(); + Assertions.assertNotNull(server); + this.server.setSpawnRadius(0); + } + + @AfterEach + public void tearDown() { + MockBukkit.unmock(); + } + + @Test + @DisplayName("Doesn't drop items if player dies in lava") + public void doesntDropItemsInLava() { + InputStream stream = getClass().getClassLoader().getResourceAsStream("drop-conditions/fire-drop.yml"); + if (stream == null) throw new IllegalStateException("Missing config"); + + DeathChestConfig config; + try (InputStreamReader reader = new InputStreamReader(stream)) { + config = DeathChestConfig.load(YamlConfiguration.loadConfiguration(reader)); + } catch (Exception e) { + throw new RuntimeException(e); + } + DeathChestPlugin plugin = MockBukkit.load(DeathChestPlugin.class, true, config); + + List list = new ArrayList<>(List.of(new ItemStack(Material.OAK_LOG))); + + PlayerMock player = server.addPlayer(); + Location location = player.getLocation(); + + // Give items + PlayerInventory inventory = player.getInventory(); + inventory.addItem(list.toArray(ItemStack[]::new)); + + // Set the player block to fire + Block block = location.getBlock(); + block.setType(Material.LAVA); + + // Kill the player + player.setHealth(0.0); + server.getScheduler().performOneTick(); + + // Check that no chest spawned + DeathChestModel lastChest = plugin.getLastChest(player); + Assertions.assertNotNull(lastChest); + } + + @Test + @DisplayName("Doesn't drop items if player dies in void") + public void doesntDropItemsInVoid() { + InputStream stream = getClass().getClassLoader().getResourceAsStream("drop-conditions/fire-drop.yml"); + if (stream == null) throw new IllegalStateException("Missing config"); + + DeathChestConfig config; + try (InputStreamReader reader = new InputStreamReader(stream)) { + config = DeathChestConfig.load(YamlConfiguration.loadConfiguration(reader)); + } catch (Exception e) { + throw new RuntimeException(e); + } + DeathChestPlugin plugin = MockBukkit.load(DeathChestPlugin.class, true, config); + + List list = new ArrayList<>(List.of(new ItemStack(Material.OAK_LOG))); + + PlayerMock player = server.addPlayer(); + Location location = player.getLocation(); + WorldMock world = player.getWorld(); + + // Give items + PlayerInventory inventory = player.getInventory(); + inventory.addItem(list.toArray(ItemStack[]::new)); + + // Teleport the player to the void + location.setY(world.getMinHeight() - 1); + player.teleport(location); + + // Kill the player + player.setHealth(0.0); + server.getScheduler().performOneTick(); + + // Check that no chest spawned + DeathChestModel lastChest = plugin.getLastChest(player); + Assertions.assertNotNull(lastChest); + } + + @Test + @DisplayName("Drop items if player dies in fire") + public void dropItemsInFire() { + InputStream stream = getClass().getClassLoader().getResourceAsStream("drop-conditions/fire-drop.yml"); + if (stream == null) throw new IllegalStateException("Missing config"); + + DeathChestConfig config; + try (InputStreamReader reader = new InputStreamReader(stream)) { + config = DeathChestConfig.load(YamlConfiguration.loadConfiguration(reader)); + } catch (Exception e) { + throw new RuntimeException(e); + } + DeathChestPlugin plugin = MockBukkit.load(DeathChestPlugin.class, true, config); + + List list = new ArrayList<>(List.of(new ItemStack(Material.OAK_LOG))); + + PlayerMock player = server.addPlayer(); + Location location = player.getLocation(); + + // Give items + PlayerInventory inventory = player.getInventory(); + inventory.addItem(list.toArray(ItemStack[]::new)); + + // Set the player block to fire + Block block = location.getBlock(); + block.setType(Material.FIRE); + + // Kill the player + player.setHealth(0.0); + server.getScheduler().performOneTick(); + + // Check that chest spawned + DeathChestModel lastChest = plugin.getLastChest(player); + Assertions.assertNull(lastChest); + } + +} diff --git a/src/test/java/com/github/devcyntrix/deathchest/dropconditions/LavaDropConditionCheck.java b/src/test/java/com/github/devcyntrix/deathchest/dropconditions/LavaDropConditionCheck.java new file mode 100644 index 0000000..d3026fb --- /dev/null +++ b/src/test/java/com/github/devcyntrix/deathchest/dropconditions/LavaDropConditionCheck.java @@ -0,0 +1,149 @@ +package com.github.devcyntrix.deathchest.dropconditions; + +import be.seeseemelk.mockbukkit.MockBukkit; +import be.seeseemelk.mockbukkit.ServerMock; +import be.seeseemelk.mockbukkit.WorldMock; +import be.seeseemelk.mockbukkit.entity.PlayerMock; +import com.github.devcyntrix.deathchest.DeathChestModel; +import com.github.devcyntrix.deathchest.DeathChestPlugin; +import com.github.devcyntrix.deathchest.config.DeathChestConfig; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.junit.jupiter.api.*; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +@DisplayName("Lava drop condition check") +public class LavaDropConditionCheck { + + private ServerMock server; + + @BeforeEach + public void setUp() { + this.server = MockBukkit.getOrCreateMock(); + Assertions.assertNotNull(server); + this.server.setSpawnRadius(0); + } + + @AfterEach + public void tearDown() { + MockBukkit.unmock(); + } + + @Test + @DisplayName("Drop items if player dies in lava") + public void dropItemsInLava() { + InputStream stream = getClass().getClassLoader().getResourceAsStream("drop-conditions/lava-drop.yml"); + if (stream == null) throw new IllegalStateException("Missing config"); + + DeathChestConfig config; + try (InputStreamReader reader = new InputStreamReader(stream)) { + config = DeathChestConfig.load(YamlConfiguration.loadConfiguration(reader)); + } catch (Exception e) { + throw new RuntimeException(e); + } + DeathChestPlugin plugin = MockBukkit.load(DeathChestPlugin.class, true, config); + + List list = new ArrayList<>(List.of(new ItemStack(Material.OAK_LOG))); + + PlayerMock player = server.addPlayer(); + Location location = player.getLocation(); + + // Give items + PlayerInventory inventory = player.getInventory(); + inventory.addItem(list.toArray(ItemStack[]::new)); + + // Set the player block to fire + Block block = location.getBlock(); + block.setType(Material.LAVA); + + // Kill the player + player.setHealth(0.0); + server.getScheduler().performOneTick(); + + // Check that no chest spawned + DeathChestModel lastChest = plugin.getLastChest(player); + Assertions.assertNull(lastChest); + } + + @Test + @DisplayName("Doesn't drop items if player dies in void") + public void doesntDropItemsInVoid() { + InputStream stream = getClass().getClassLoader().getResourceAsStream("drop-conditions/lava-drop.yml"); + if (stream == null) throw new IllegalStateException("Missing config"); + + DeathChestConfig config; + try (InputStreamReader reader = new InputStreamReader(stream)) { + config = DeathChestConfig.load(YamlConfiguration.loadConfiguration(reader)); + } catch (Exception e) { + throw new RuntimeException(e); + } + DeathChestPlugin plugin = MockBukkit.load(DeathChestPlugin.class, true, config); + + List list = new ArrayList<>(List.of(new ItemStack(Material.OAK_LOG))); + + PlayerMock player = server.addPlayer(); + Location location = player.getLocation(); + WorldMock world = player.getWorld(); + + // Give items + PlayerInventory inventory = player.getInventory(); + inventory.addItem(list.toArray(ItemStack[]::new)); + + // Teleport the player to the void + location.setY(world.getMinHeight() - 1); + player.teleport(location); + + // Kill the player + player.setHealth(0.0); + server.getScheduler().performOneTick(); + + // Check that no chest spawned + DeathChestModel lastChest = plugin.getLastChest(player); + Assertions.assertNotNull(lastChest); + } + + @Test + @DisplayName("Doesn't drop items if player dies in fire") + public void doesntDropItemsInFire() { + InputStream stream = getClass().getClassLoader().getResourceAsStream("drop-conditions/lava-drop.yml"); + if (stream == null) throw new IllegalStateException("Missing config"); + + DeathChestConfig config; + try (InputStreamReader reader = new InputStreamReader(stream)) { + config = DeathChestConfig.load(YamlConfiguration.loadConfiguration(reader)); + } catch (Exception e) { + throw new RuntimeException(e); + } + DeathChestPlugin plugin = MockBukkit.load(DeathChestPlugin.class, true, config); + + List list = new ArrayList<>(List.of(new ItemStack(Material.OAK_LOG))); + + PlayerMock player = server.addPlayer(); + Location location = player.getLocation(); + + // Give items + PlayerInventory inventory = player.getInventory(); + inventory.addItem(list.toArray(ItemStack[]::new)); + + // Set the player block to fire + Block block = location.getBlock(); + block.setType(Material.FIRE); + + // Kill the player + player.setHealth(0.0); + server.getScheduler().performOneTick(); + + // Check that chest spawned + DeathChestModel lastChest = plugin.getLastChest(player); + Assertions.assertNotNull(lastChest); + } + +} diff --git a/src/test/java/com/github/devcyntrix/deathchest/dropconditions/VoidDropConditionCheck.java b/src/test/java/com/github/devcyntrix/deathchest/dropconditions/VoidDropConditionCheck.java new file mode 100644 index 0000000..4fe7d3a --- /dev/null +++ b/src/test/java/com/github/devcyntrix/deathchest/dropconditions/VoidDropConditionCheck.java @@ -0,0 +1,149 @@ +package com.github.devcyntrix.deathchest.dropconditions; + +import be.seeseemelk.mockbukkit.MockBukkit; +import be.seeseemelk.mockbukkit.ServerMock; +import be.seeseemelk.mockbukkit.WorldMock; +import be.seeseemelk.mockbukkit.entity.PlayerMock; +import com.github.devcyntrix.deathchest.DeathChestModel; +import com.github.devcyntrix.deathchest.DeathChestPlugin; +import com.github.devcyntrix.deathchest.config.DeathChestConfig; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.junit.jupiter.api.*; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +@DisplayName("Void drop condition check") +public class VoidDropConditionCheck { + + private ServerMock server; + + @BeforeEach + public void setUp() { + this.server = MockBukkit.getOrCreateMock(); + Assertions.assertNotNull(server); + this.server.setSpawnRadius(0); + } + + @AfterEach + public void tearDown() { + MockBukkit.unmock(); + } + + @Test + @DisplayName("Doesn't drop items if player dies in lava") + public void doesntDropItemsInLava() { + InputStream stream = getClass().getClassLoader().getResourceAsStream("drop-conditions/void-drop.yml"); + if (stream == null) throw new IllegalStateException("Missing config"); + + DeathChestConfig config; + try (InputStreamReader reader = new InputStreamReader(stream)) { + config = DeathChestConfig.load(YamlConfiguration.loadConfiguration(reader)); + } catch (Exception e) { + throw new RuntimeException(e); + } + DeathChestPlugin plugin = MockBukkit.load(DeathChestPlugin.class, true, config); + + List list = new ArrayList<>(List.of(new ItemStack(Material.OAK_LOG))); + + PlayerMock player = server.addPlayer(); + Location location = player.getLocation(); + + // Give items + PlayerInventory inventory = player.getInventory(); + inventory.addItem(list.toArray(ItemStack[]::new)); + + // Set the player block to fire + Block block = location.getBlock(); + block.setType(Material.LAVA); + + // Kill the player + player.setHealth(0.0); + server.getScheduler().performOneTick(); + + // Check that no chest spawned + DeathChestModel lastChest = plugin.getLastChest(player); + Assertions.assertNotNull(lastChest); + } + + @Test + @DisplayName("Drop items if player dies in void") + public void dropItemsInVoid() { + InputStream stream = getClass().getClassLoader().getResourceAsStream("drop-conditions/void-drop.yml"); + if (stream == null) throw new IllegalStateException("Missing config"); + + DeathChestConfig config; + try (InputStreamReader reader = new InputStreamReader(stream)) { + config = DeathChestConfig.load(YamlConfiguration.loadConfiguration(reader)); + } catch (Exception e) { + throw new RuntimeException(e); + } + DeathChestPlugin plugin = MockBukkit.load(DeathChestPlugin.class, true, config); + + List list = new ArrayList<>(List.of(new ItemStack(Material.OAK_LOG))); + + PlayerMock player = server.addPlayer(); + Location location = player.getLocation(); + WorldMock world = player.getWorld(); + + // Give items + PlayerInventory inventory = player.getInventory(); + inventory.addItem(list.toArray(ItemStack[]::new)); + + // Teleport the player to the void + location.setY(world.getMinHeight() - 1); + player.teleport(location); + + // Kill the player + player.setHealth(0.0); + server.getScheduler().performOneTick(); + + // Check that no chest spawned + DeathChestModel lastChest = plugin.getLastChest(player); + Assertions.assertNull(lastChest); + } + + @Test + @DisplayName("Doesn't drop items if player dies in fire") + public void doesntDropItemsInFire() { + InputStream stream = getClass().getClassLoader().getResourceAsStream("drop-conditions/void-drop.yml"); + if (stream == null) throw new IllegalStateException("Missing config"); + + DeathChestConfig config; + try (InputStreamReader reader = new InputStreamReader(stream)) { + config = DeathChestConfig.load(YamlConfiguration.loadConfiguration(reader)); + } catch (Exception e) { + throw new RuntimeException(e); + } + DeathChestPlugin plugin = MockBukkit.load(DeathChestPlugin.class, true, config); + + List list = new ArrayList<>(List.of(new ItemStack(Material.OAK_LOG))); + + PlayerMock player = server.addPlayer(); + Location location = player.getLocation(); + + // Give items + PlayerInventory inventory = player.getInventory(); + inventory.addItem(list.toArray(ItemStack[]::new)); + + // Set the player block to fire + Block block = location.getBlock(); + block.setType(Material.FIRE); + + // Kill the player + player.setHealth(0.0); + server.getScheduler().performOneTick(); + + // Check that chest spawned + DeathChestModel lastChest = plugin.getLastChest(player); + Assertions.assertNotNull(lastChest); + } + +} diff --git a/src/test/java/com/github/devcyntrix/deathchest/protection/SpawnRadiusCheck.java b/src/test/java/com/github/devcyntrix/deathchest/protection/SpawnRadiusCheck.java new file mode 100644 index 0000000..3474e3a --- /dev/null +++ b/src/test/java/com/github/devcyntrix/deathchest/protection/SpawnRadiusCheck.java @@ -0,0 +1,81 @@ +package com.github.devcyntrix.deathchest.protection; + +import be.seeseemelk.mockbukkit.MockBukkit; +import be.seeseemelk.mockbukkit.ServerMock; +import be.seeseemelk.mockbukkit.WorldMock; +import be.seeseemelk.mockbukkit.entity.PlayerMock; +import com.github.devcyntrix.deathchest.DeathChestPlugin; +import com.github.devcyntrix.deathchest.config.DeathChestConfig; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.*; + +import java.io.InputStream; +import java.io.InputStreamReader; + +@DisplayName("Spawn radius check") +public class SpawnRadiusCheck { + + private ServerMock server; + + @BeforeEach + public void setUp() { + InputStream stream = getClass().getClassLoader().getResourceAsStream("default-config.yml"); + if (stream == null) throw new IllegalStateException("Missing config"); + + DeathChestConfig config; + try (InputStreamReader reader = new InputStreamReader(stream)) { + config = DeathChestConfig.load(YamlConfiguration.loadConfiguration(reader)); + } catch (Exception e) { + throw new RuntimeException(e); + } + + this.server = MockBukkit.getOrCreateMock(); + MockBukkit.load(DeathChestPlugin.class, true, config); + } + + @AfterEach + public void tearDown() { + MockBukkit.unmock(); + } + + @Test + @DisplayName("Cannot spawn chest in spawn radius") + public void doesntSpawnInRadius() { + PlayerMock player = server.addPlayer(); + ItemStack[] item = new ItemStack[]{new ItemStack(Material.OAK_LOG)}; + + for (int x = -16; x <= 16; x++) { + for (int z = -16; z <= 16; z++) { + WorldMock world = player.getWorld(); + @NotNull Location location = new Location(world, x, world.getHighestBlockYAt(x, z) + 1, z); + player.teleport(location); + player.getInventory().addItem(item); + player.setHealth(0.0); + server.getScheduler().performOneTick(); + Assertions.assertEquals(Material.AIR, location.getBlock().getType()); + player.respawn(); + } + } + } + + @Test + @DisplayName("Spawn chest in spawn radius") + public void spawnInRadius() { + PlayerMock player = server.addPlayer(); + ItemStack[] item = new ItemStack[]{new ItemStack(Material.OAK_LOG)}; + player.getInventory().addItem(item); + + WorldMock world = player.getWorld(); + + Location location = new Location(world, 17, world.getHighestBlockYAt(17, 17) + 1, 17); + player.teleport(location); // 17 because the default spawn radius is 16 blocks + player.setHealth(0.0); + server.getScheduler().performOneTick(); + Assertions.assertEquals(Material.CHEST, location.getBlock().getType()); + } + +} diff --git a/src/test/resources/drop-conditions/fire-drop.yml b/src/test/resources/drop-conditions/fire-drop.yml new file mode 100644 index 0000000..3637390 --- /dev/null +++ b/src/test/resources/drop-conditions/fire-drop.yml @@ -0,0 +1,140 @@ +# +# The Configuration file of Death Chest +# In this file you can customize the plugin. +# + +# I recommend leaving this on true to receive notification if I published an update. +update-checker: true +auto-update: true + +duration-format: 'mm:ss' + +chest: + # The expiration of the chest in seconds. + # You can deactivate the expiration by setting the value to -1 + expiration: 600 + no-expiration-permission: + enabled: false + permission: 'deathchest.stays-forever' + # If this feature is enabled all caught items will be dropped if the chest has expired. + drop-items-after-expiration: false + # Set this option to true if you want to protect the death chests against explosions. + blast-protection: false + + # This option protects the death chest against thieves if the player has the specific permission and the thief + # not the bypass permission. + thief-protection: + # Set the value to 'true' if you want to activate this feature + enabled: false + permission: 'deathchest.thiefprotected' + bypass-permission: 'deathchest.thiefprotected.bypass' + # To enable the expiration set the number to number greater than 0. The unit of this variable is seconds. + expiration: 0 + # On this website you can find a list of all sounds which is implemented in SpigotMC + # @see https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Sound.html + # If you want to disable the sound or the message, you can delete these options + sound: BLOCK_CHEST_LOCKED;1.0;1.0 + message: |- + &cYou are not permitted to open this chest + +# Here you can modify the inventory which opens when you right-click the death chest. You can use color codes with '&' +# in the title and change the inventory size by changing the size value. +inventory: + # Here you can use the Placeholder API and + # ${player_name}: Name + # ${player_displayname}: Displayname + # ${duration}: Time left + title: 'Death Chest' + # Possible values are: 'constant' and 'flexible' + # constant: the size of the inventory is always 45 slots large + # flexible: the size of the inventory depends on the items which had the player in the inventory. + size: flexible + +# This section is for handling the hologram function in this plugin. You can remove this section to disable the feature, +# or you set the 'enabled' option to false +# This feature is only available if you have a supported hologram plugin installed. +hologram: + # Set the value to 'false' if you want to deactivate this feature + enabled: true + # If you add lines you have to increase this value. This values depends on the hologram + # plugin and the distance between the plugin + height: 1 + line-height: 0.25 + # Support for ${player_name}: Name + # ${player_displayname}: Displayname + # ${duration}: Time left + lines: + - '&7&lR.I.P' + - '${player_name}' + - '&3-&6-&3-&6-&3-&6-&3-' + - '${duration}' + +# This section is for handling the particle function in this plugin. You can remove this section to disable the feature, +# or you set the 'enabled' option to false +particle: + # Set the value to 'false' if you want to deactivate this feature + enabled: true + radius: 1.0 + count: 32 + # Maximum is 20 + speed: 20.0 + +# This section is for handling the block breaking function in this plugin. You can remove this section to disable the feature, +# or you set the 'enabled' option to false +# This feature is only available if you have ProtocolLib installed. +break-effect: + # Set the value to 'false' if you want to deactivate this feature + enabled: true + view-distance: 20.0 + +# This section is for handling the notification function in this plugin. You can remove this section to disable the feature, +# or you set the 'enabled' option to false. +# This feature sends a message to the dead player. Here you can inform the player about the chest and their expiration. +# You can send the death coordination by using the placeholders: ${x} ${y}, ${z} and ${world} +# Also you can send the chest coordination by using the placeholders: ${chest_x}, ${chest_y} and ${chest_z} +player-notification: + # Set the value to 'false' if you want to deactivate this feature + enabled: true + message: |- + &7You died. Your items were put into a chest which disappears after &c10 minutes&7! ${x} ${y} ${z} + +# Available Placeholders: +# ${player_name} : shows the player name +# ${player_displayname} : shows the player display name (Maybe with prefix) +# ${x}, ${y}, ${z}, ${world} : shows the coordinates of the death location +# ${chest_x}, ${chest_y}, ${chest_z} : shows the coordinates of the chest +global-notification: + # Set the value to 'true' if you want to activate this feature + enabled: false + message: |- + &7${player_name} died at ${x} ${y} ${z} in ${world} + +# Available Placeholders: +# ${player_name} : shows the player name +# ${player_displayname} : shows the player display name (Maybe with prefix) +# ${x}, ${y}, ${z}, ${world} : shows the coordinates of the death location +# (coordinates of the chest isn't available for here) +# To disable the death message change "enabled" to true and remove the message option. +change-death-message: + # Set the value to 'true' if you want to activate this feature + enabled: false + message: |- + &7${player_name} died at ${x} ${y} ${z} in ${world} + +# This section configures the activation of this plugin for specific worlds. +world-filter: + # Possible values: blacklist, whitelist + filter: blacklist + worlds: + - disabled_world + +# Drops the items if any of the conditions becomes the true when activated. +drop-conditions: + lava-death: false + fire-death: true + void-death: false + +# Do not touch this! +config-version: 3 +# Prints debug messages if property is set to true +debug: false \ No newline at end of file diff --git a/src/test/resources/drop-conditions/lava-drop.yml b/src/test/resources/drop-conditions/lava-drop.yml new file mode 100644 index 0000000..4b10d01 --- /dev/null +++ b/src/test/resources/drop-conditions/lava-drop.yml @@ -0,0 +1,140 @@ +# +# The Configuration file of Death Chest +# In this file you can customize the plugin. +# + +# I recommend leaving this on true to receive notification if I published an update. +update-checker: true +auto-update: true + +duration-format: 'mm:ss' + +chest: + # The expiration of the chest in seconds. + # You can deactivate the expiration by setting the value to -1 + expiration: 600 + no-expiration-permission: + enabled: false + permission: 'deathchest.stays-forever' + # If this feature is enabled all caught items will be dropped if the chest has expired. + drop-items-after-expiration: false + # Set this option to true if you want to protect the death chests against explosions. + blast-protection: false + + # This option protects the death chest against thieves if the player has the specific permission and the thief + # not the bypass permission. + thief-protection: + # Set the value to 'true' if you want to activate this feature + enabled: false + permission: 'deathchest.thiefprotected' + bypass-permission: 'deathchest.thiefprotected.bypass' + # To enable the expiration set the number to number greater than 0. The unit of this variable is seconds. + expiration: 0 + # On this website you can find a list of all sounds which is implemented in SpigotMC + # @see https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Sound.html + # If you want to disable the sound or the message, you can delete these options + sound: BLOCK_CHEST_LOCKED;1.0;1.0 + message: |- + &cYou are not permitted to open this chest + +# Here you can modify the inventory which opens when you right-click the death chest. You can use color codes with '&' +# in the title and change the inventory size by changing the size value. +inventory: + # Here you can use the Placeholder API and + # ${player_name}: Name + # ${player_displayname}: Displayname + # ${duration}: Time left + title: 'Death Chest' + # Possible values are: 'constant' and 'flexible' + # constant: the size of the inventory is always 45 slots large + # flexible: the size of the inventory depends on the items which had the player in the inventory. + size: flexible + +# This section is for handling the hologram function in this plugin. You can remove this section to disable the feature, +# or you set the 'enabled' option to false +# This feature is only available if you have a supported hologram plugin installed. +hologram: + # Set the value to 'false' if you want to deactivate this feature + enabled: true + # If you add lines you have to increase this value. This values depends on the hologram + # plugin and the distance between the plugin + height: 1 + line-height: 0.25 + # Support for ${player_name}: Name + # ${player_displayname}: Displayname + # ${duration}: Time left + lines: + - '&7&lR.I.P' + - '${player_name}' + - '&3-&6-&3-&6-&3-&6-&3-' + - '${duration}' + +# This section is for handling the particle function in this plugin. You can remove this section to disable the feature, +# or you set the 'enabled' option to false +particle: + # Set the value to 'false' if you want to deactivate this feature + enabled: true + radius: 1.0 + count: 32 + # Maximum is 20 + speed: 20.0 + +# This section is for handling the block breaking function in this plugin. You can remove this section to disable the feature, +# or you set the 'enabled' option to false +# This feature is only available if you have ProtocolLib installed. +break-effect: + # Set the value to 'false' if you want to deactivate this feature + enabled: true + view-distance: 20.0 + +# This section is for handling the notification function in this plugin. You can remove this section to disable the feature, +# or you set the 'enabled' option to false. +# This feature sends a message to the dead player. Here you can inform the player about the chest and their expiration. +# You can send the death coordination by using the placeholders: ${x} ${y}, ${z} and ${world} +# Also you can send the chest coordination by using the placeholders: ${chest_x}, ${chest_y} and ${chest_z} +player-notification: + # Set the value to 'false' if you want to deactivate this feature + enabled: true + message: |- + &7You died. Your items were put into a chest which disappears after &c10 minutes&7! ${x} ${y} ${z} + +# Available Placeholders: +# ${player_name} : shows the player name +# ${player_displayname} : shows the player display name (Maybe with prefix) +# ${x}, ${y}, ${z}, ${world} : shows the coordinates of the death location +# ${chest_x}, ${chest_y}, ${chest_z} : shows the coordinates of the chest +global-notification: + # Set the value to 'true' if you want to activate this feature + enabled: false + message: |- + &7${player_name} died at ${x} ${y} ${z} in ${world} + +# Available Placeholders: +# ${player_name} : shows the player name +# ${player_displayname} : shows the player display name (Maybe with prefix) +# ${x}, ${y}, ${z}, ${world} : shows the coordinates of the death location +# (coordinates of the chest isn't available for here) +# To disable the death message change "enabled" to true and remove the message option. +change-death-message: + # Set the value to 'true' if you want to activate this feature + enabled: false + message: |- + &7${player_name} died at ${x} ${y} ${z} in ${world} + +# This section configures the activation of this plugin for specific worlds. +world-filter: + # Possible values: blacklist, whitelist + filter: blacklist + worlds: + - disabled_world + +# Drops the items if any of the conditions becomes the true when activated. +drop-conditions: + lava-death: true + fire-death: false + void-death: false + +# Do not touch this! +config-version: 3 +# Prints debug messages if property is set to true +debug: false \ No newline at end of file diff --git a/src/test/resources/drop-conditions/void-drop.yml b/src/test/resources/drop-conditions/void-drop.yml new file mode 100644 index 0000000..f7dd62b --- /dev/null +++ b/src/test/resources/drop-conditions/void-drop.yml @@ -0,0 +1,140 @@ +# +# The Configuration file of Death Chest +# In this file you can customize the plugin. +# + +# I recommend leaving this on true to receive notification if I published an update. +update-checker: true +auto-update: true + +duration-format: 'mm:ss' + +chest: + # The expiration of the chest in seconds. + # You can deactivate the expiration by setting the value to -1 + expiration: 600 + no-expiration-permission: + enabled: false + permission: 'deathchest.stays-forever' + # If this feature is enabled all caught items will be dropped if the chest has expired. + drop-items-after-expiration: false + # Set this option to true if you want to protect the death chests against explosions. + blast-protection: false + + # This option protects the death chest against thieves if the player has the specific permission and the thief + # not the bypass permission. + thief-protection: + # Set the value to 'true' if you want to activate this feature + enabled: false + permission: 'deathchest.thiefprotected' + bypass-permission: 'deathchest.thiefprotected.bypass' + # To enable the expiration set the number to number greater than 0. The unit of this variable is seconds. + expiration: 0 + # On this website you can find a list of all sounds which is implemented in SpigotMC + # @see https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Sound.html + # If you want to disable the sound or the message, you can delete these options + sound: BLOCK_CHEST_LOCKED;1.0;1.0 + message: |- + &cYou are not permitted to open this chest + +# Here you can modify the inventory which opens when you right-click the death chest. You can use color codes with '&' +# in the title and change the inventory size by changing the size value. +inventory: + # Here you can use the Placeholder API and + # ${player_name}: Name + # ${player_displayname}: Displayname + # ${duration}: Time left + title: 'Death Chest' + # Possible values are: 'constant' and 'flexible' + # constant: the size of the inventory is always 45 slots large + # flexible: the size of the inventory depends on the items which had the player in the inventory. + size: flexible + +# This section is for handling the hologram function in this plugin. You can remove this section to disable the feature, +# or you set the 'enabled' option to false +# This feature is only available if you have a supported hologram plugin installed. +hologram: + # Set the value to 'false' if you want to deactivate this feature + enabled: true + # If you add lines you have to increase this value. This values depends on the hologram + # plugin and the distance between the plugin + height: 1 + line-height: 0.25 + # Support for ${player_name}: Name + # ${player_displayname}: Displayname + # ${duration}: Time left + lines: + - '&7&lR.I.P' + - '${player_name}' + - '&3-&6-&3-&6-&3-&6-&3-' + - '${duration}' + +# This section is for handling the particle function in this plugin. You can remove this section to disable the feature, +# or you set the 'enabled' option to false +particle: + # Set the value to 'false' if you want to deactivate this feature + enabled: true + radius: 1.0 + count: 32 + # Maximum is 20 + speed: 20.0 + +# This section is for handling the block breaking function in this plugin. You can remove this section to disable the feature, +# or you set the 'enabled' option to false +# This feature is only available if you have ProtocolLib installed. +break-effect: + # Set the value to 'false' if you want to deactivate this feature + enabled: true + view-distance: 20.0 + +# This section is for handling the notification function in this plugin. You can remove this section to disable the feature, +# or you set the 'enabled' option to false. +# This feature sends a message to the dead player. Here you can inform the player about the chest and their expiration. +# You can send the death coordination by using the placeholders: ${x} ${y}, ${z} and ${world} +# Also you can send the chest coordination by using the placeholders: ${chest_x}, ${chest_y} and ${chest_z} +player-notification: + # Set the value to 'false' if you want to deactivate this feature + enabled: true + message: |- + &7You died. Your items were put into a chest which disappears after &c10 minutes&7! ${x} ${y} ${z} + +# Available Placeholders: +# ${player_name} : shows the player name +# ${player_displayname} : shows the player display name (Maybe with prefix) +# ${x}, ${y}, ${z}, ${world} : shows the coordinates of the death location +# ${chest_x}, ${chest_y}, ${chest_z} : shows the coordinates of the chest +global-notification: + # Set the value to 'true' if you want to activate this feature + enabled: false + message: |- + &7${player_name} died at ${x} ${y} ${z} in ${world} + +# Available Placeholders: +# ${player_name} : shows the player name +# ${player_displayname} : shows the player display name (Maybe with prefix) +# ${x}, ${y}, ${z}, ${world} : shows the coordinates of the death location +# (coordinates of the chest isn't available for here) +# To disable the death message change "enabled" to true and remove the message option. +change-death-message: + # Set the value to 'true' if you want to activate this feature + enabled: false + message: |- + &7${player_name} died at ${x} ${y} ${z} in ${world} + +# This section configures the activation of this plugin for specific worlds. +world-filter: + # Possible values: blacklist, whitelist + filter: blacklist + worlds: + - disabled_world + +# Drops the items if any of the conditions becomes the true when activated. +drop-conditions: + lava-death: false + fire-death: false + void-death: true + +# Do not touch this! +config-version: 3 +# Prints debug messages if property is set to true +debug: false \ No newline at end of file