From 124b4d706f9589d0abc4ba38b68866f0935d3008 Mon Sep 17 00:00:00 2001 From: Rhythmic System Date: Thu, 3 Apr 2025 13:40:23 -0700 Subject: [PATCH 01/11] Fix issue where 'use-as-blacklist' would only work on plugin reload --- build.gradle.kts | 2 +- src/main/java/simplexity/simplebucketmobs/config/Config.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 2c055aa..70ddbdb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,4 @@ -version = "1.2" +version = "1.2.1" plugins { `java-library` diff --git a/src/main/java/simplexity/simplebucketmobs/config/Config.java b/src/main/java/simplexity/simplebucketmobs/config/Config.java index e4000ef..e007fde 100644 --- a/src/main/java/simplexity/simplebucketmobs/config/Config.java +++ b/src/main/java/simplexity/simplebucketmobs/config/Config.java @@ -50,11 +50,11 @@ public boolean isNoHostileTargeting() { public void reloadConfig() { SimpleBucketMobs.getPlugin().reloadConfig(); FileConfiguration config = SimpleBucketMobs.getPlugin().getConfig(); - setupTypes(config); bucketTitle = config.getString("bucket-title", " Bucket"); noHostileTargeting = config.getBoolean("no-hostile-targeting", true); listIsBlacklist = config.getBoolean("list-is-blacklist", false); useResourcePack = config.getBoolean("use-resource-pack", true); + setupTypes(config); } private void setupTypes(FileConfiguration config) { From f75564586fa748a7d09573bcb47fddce855254fd Mon Sep 17 00:00:00 2001 From: Rhythmic System Date: Tue, 22 Jul 2025 13:45:22 -0700 Subject: [PATCH 02/11] technically works - also deletes other buckets in the stack --- build.gradle.kts | 10 +- gradle/wrapper/gradle-wrapper.properties | 2 +- output.json | 110 +++++++ .../command/subcommand/Debucket.java | 2 +- .../simplebucketmobs/config/Texture.java | 158 +++++----- .../simplebucketmobs/listener/BucketMob.java | 298 +++++------------- src/main/resources/plugin.yml | 4 +- 7 files changed, 285 insertions(+), 299 deletions(-) create mode 100644 output.json diff --git a/build.gradle.kts b/build.gradle.kts index 70ddbdb..c7f4685 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,8 +2,8 @@ version = "1.2.1" plugins { `java-library` - id("io.papermc.paperweight.userdev") version "2.0.0-beta.8" - id("xyz.jpenilla.run-paper") version "2.3.1" // Adds runServer and runMojangMappedServer tasks for testing + id("io.papermc.paperweight.userdev") version "2.0.0-beta.17" + } java { @@ -15,12 +15,12 @@ repositories { } dependencies { - paperweight.paperDevBundle("1.21.4-R0.1-SNAPSHOT") + paperweight.paperDevBundle("1.21.7-R0.1-SNAPSHOT") } tasks { assemble { - dependsOn("reobfJar") + paperweight.reobfArtifactConfiguration = io.papermc.paperweight.userdev.ReobfArtifactConfiguration.MOJANG_PRODUCTION } compileJava { @@ -36,4 +36,4 @@ tasks { expand("version" to project.version) } } -} \ No newline at end of file +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cea7a79..ca025c8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/output.json b/output.json new file mode 100644 index 0000000..61e61af --- /dev/null +++ b/output.json @@ -0,0 +1,110 @@ +{ + AbsorptionAmount: 0.0f, + Age: 0, + AgeLocked: 0b, + Air: 300s, + ArmorDropChances: [ + 0.085f, + 0.085f, + 0.085f, + 0.085f + ], + ArmorItems: [ + {}, + {}, + {}, + {} + ], + Brain: { + memories: {} + }, + Bukkit.Aware: 1b, + Bukkit.updateLevel: 2, + CanPickUpLoot: 0b, + DeathTime: 0s, + FallDistance: 0.0f, + FallFlying: 0b, + Fire: -1s, + ForcedAge: 0, + HandDropChances: [ + 0.085f, + 0.085f + ], + HandItems: [ + {}, + {} + ], + Health: 10.0f, + HurtByTimestamp: 0, + HurtTime: 0s, + InLove: 0, + Invulnerable: 0b, + LeftHanded: 0b, + Motion: [ + 0.0d, + -0.0784000015258789d, + 0.0d + ], + OnGround: 1b, + Paper.Origin: [ + 5.5d, + 63.0d, + -100.5d + ], + Paper.OriginWorld: [ + I;-1033631177, + -379630048, + -1565680706, + -46542190 + ], + Paper.SpawnReason: "SPAWNER_EGG", + PersistenceRequired: 0b, + PortalCooldown: 0, + Pos: [ + 5.5d, + 63.0d, + -100.5d + ], + Purpur.ShouldBurnInDay: 0b, + Purpur.ticksSinceLastInteraction: 0, + Rotation: [ + -35.886017f, + -11.023556f + ], + Saddle: 0b, + Spigot.ticksLived: 26, + UUID: [ + I;-1326762373, + -430356289, + -1868207975, + 1454220499 + ], + WorldUUIDLeast: -6724547423999765870L, + WorldUUIDMost: -4439412097425650144L, + attributes: [ + { + base: 0.25d, + id: "minecraft:movement_speed" + }, + { + base: 16.0d, + id: "minecraft:follow_range", + modifiers: [ + { + amount: -0.027825571221113208d, + id: "minecraft:random_spawn_bonus", + operation: "add_multiplied_base" + } + ] + }, + { + base: 1.0d, + id: "minecraft:scale" + }, + { + base: 10.0d, + id: "minecraft:max_health" + } + ], + id: "minecraft:pig" +} diff --git a/src/main/java/simplexity/simplebucketmobs/command/subcommand/Debucket.java b/src/main/java/simplexity/simplebucketmobs/command/subcommand/Debucket.java index a3a22e0..38000db 100644 --- a/src/main/java/simplexity/simplebucketmobs/command/subcommand/Debucket.java +++ b/src/main/java/simplexity/simplebucketmobs/command/subcommand/Debucket.java @@ -30,7 +30,7 @@ public void execute(CommandSender sender, String[] args) { } ItemStack item = player.getInventory().getItem(EquipmentSlot.HAND); PersistentDataContainer pdc = item.getItemMeta().getPersistentDataContainer(); - String nbt = pdc.get(BucketMob.mobNBTKey, PersistentDataType.STRING); + String nbt = pdc.get(BucketMob.legacyMobTag, PersistentDataType.STRING); if (nbt == null) { player.sendMessage(Message.ERROR_NO_BUCKET_MOB.getParsedMessage()); return; diff --git a/src/main/java/simplexity/simplebucketmobs/config/Texture.java b/src/main/java/simplexity/simplebucketmobs/config/Texture.java index d9b0111..09cb16d 100644 --- a/src/main/java/simplexity/simplebucketmobs/config/Texture.java +++ b/src/main/java/simplexity/simplebucketmobs/config/Texture.java @@ -39,84 +39,84 @@ public void reloadTextureConfig() { catch (IOException | InvalidConfigurationException e) { e.printStackTrace(); } } - public void setCustomData(EntityType type, ItemMeta meta, CompoundTag tag) { - ConfigurationSection section = texture.getConfigurationSection(type.toString()); - if (section == null) return; - setNewItemModel(meta, section); - if (surprise(type, meta, tag)) return; - String value = null; - // Until we hit a dead end or found the path... - while (true) { - String ymlKey = null; - // Find a valid key to look into. - for (String key : section.getKeys(false)) { - // If the tag does not exist, ignore this value. - if (!tag.contains(key)) continue; - ymlKey = key; - break; - } - if (ymlKey == null) break; - // If the key does not lead to a configuration section, we cannot continue. - if (!section.isConfigurationSection(ymlKey)) { - break; - } - // If the tag is not a CompoundTag, this is the value we need. - // TODO: Make it so if a value "exists" it is true, not just that the value is set to a specific one. - if (!tag.contains(ymlKey, 10)) { - Tag currentTag = tag.get(ymlKey); - assert currentTag != null; // Guaranteed, we checked. - value = currentTag.getAsString(); - section = section.getConfigurationSection(ymlKey); - break; - } - // Otherwise, go deeper... - section = section.getConfigurationSection(ymlKey); - tag = tag.getCompound(ymlKey); - assert section != null; // Guaranteed, ymlKey was pulled out of the keySet and the set was unmodified. - } - if (value == null) return; - assert section != null; // Guaranteed, ymlKey was pulled out of the keySet and the set was unmodified. - setNewItemModel(value, meta, section); - } - - private boolean surprise(EntityType type, ItemMeta meta, CompoundTag tag) { - ConfigurationSection section = texture.getConfigurationSection("special"); - if (section == null) return false; - if (type == EntityType.CHICKEN && tag.getShort("Fire") > 0) { - setNewItemModel("fried", meta, section); - return true; - } - // Dog having owner workaround. TODO: Be smarter and figure a solution. - /*if (type == EntityType.WOLF && tag.hasUUID("Owner")) { - meta.setCustomModelData(section.getInt("tamed_wolf", 0)); - return true; - } - // Toast Rabbit workaround. - TODO: Fix Toast Rabbit Workaround or be smarter and figure a solution. - if (type == EntityType.RABBIT) { - System.out.println("RABBIT"); - Tag name = tag.get("CustomName"); - if (name != null) { - System.out.println("RABBIT NAME"); - Component nameComponent = SimpleBucketMobs.getGsonSerializer().deserialize(name.toString()); - if (SimpleBucketMobs.getMiniMessage().stripTags(nameString).equals("Toast")) { - System.out.println("RABBIT NAME TOAST"); - meta.setCustomModelData(section.getInt("toast", 0)); - return true; - } - } - } - */ - return false; - } - private void setNewItemModel(ItemMeta meta, ConfigurationSection section) { - setNewItemModel("default", meta, section); - } - private void setNewItemModel(String key, ItemMeta meta, ConfigurationSection section) { - String modelLocation = section.getString(key, "minecraft:bucket"); - String[] split = modelLocation.split(":"); - NamespacedKey namespacedKey = new NamespacedKey(split[0], split[1]); - meta.setItemModel(namespacedKey); - } +// public void setCustomData(EntityType type, ItemMeta meta, CompoundTag tag) { +// ConfigurationSection section = texture.getConfigurationSection(type.toString()); +// if (section == null) return; +// setNewItemModel(meta, section); +// if (surprise(type, meta, tag)) return; +// String value = null; +// // Until we hit a dead end or found the path... +// while (true) { +// String ymlKey = null; +// // Find a valid key to look into. +// for (String key : section.getKeys(false)) { +// // If the tag does not exist, ignore this value. +// if (!tag.contains(key)) continue; +// ymlKey = key; +// break; +// } +// if (ymlKey == null) break; +// // If the key does not lead to a configuration section, we cannot continue. +// if (!section.isConfigurationSection(ymlKey)) { +// break; +// } +// // If the tag is not a CompoundTag, this is the value we need. +// // TODO: Make it so if a value "exists" it is true, not just that the value is set to a specific one. +// if (!tag.contains(ymlKey, 10)) { +// Tag currentTag = tag.get(ymlKey); +// assert currentTag != null; // Guaranteed, we checked. +// value = currentTag.getAsString(); +// section = section.getConfigurationSection(ymlKey); +// break; +// } +// // Otherwise, go deeper... +// section = section.getConfigurationSection(ymlKey); +// tag = tag.getCompound(ymlKey); +// assert section != null; // Guaranteed, ymlKey was pulled out of the keySet and the set was unmodified. +// } +// if (value == null) return; +// assert section != null; // Guaranteed, ymlKey was pulled out of the keySet and the set was unmodified. +// setNewItemModel(value, meta, section); +// } +// +// private boolean surprise(EntityType type, ItemMeta meta, CompoundTag tag) { +// ConfigurationSection section = texture.getConfigurationSection("special"); +// if (section == null) return false; +// if (type == EntityType.CHICKEN && tag.getShort("Fire") > 0) { +// setNewItemModel("fried", meta, section); +// return true; +// } +// // Dog having owner workaround. TODO: Be smarter and figure a solution. +// /*if (type == EntityType.WOLF && tag.hasUUID("Owner")) { +// meta.setCustomModelData(section.getInt("tamed_wolf", 0)); +// return true; +// } +// // Toast Rabbit workaround. +// TODO: Fix Toast Rabbit Workaround or be smarter and figure a solution. +// if (type == EntityType.RABBIT) { +// System.out.println("RABBIT"); +// Tag name = tag.get("CustomName"); +// if (name != null) { +// System.out.println("RABBIT NAME"); +// Component nameComponent = SimpleBucketMobs.getGsonSerializer().deserialize(name.toString()); +// if (SimpleBucketMobs.getMiniMessage().stripTags(nameString).equals("Toast")) { +// System.out.println("RABBIT NAME TOAST"); +// meta.setCustomModelData(section.getInt("toast", 0)); +// return true; +// } +// } +// } +// */ +// return false; +// } +// private void setNewItemModel(ItemMeta meta, ConfigurationSection section) { +// setNewItemModel("default", meta, section); +// } +// private void setNewItemModel(String key, ItemMeta meta, ConfigurationSection section) { +// String modelLocation = section.getString(key, "minecraft:bucket"); +// String[] split = modelLocation.split(":"); +// NamespacedKey namespacedKey = new NamespacedKey(split[0], split[1]); +// meta.setItemModel(namespacedKey); +// } } diff --git a/src/main/java/simplexity/simplebucketmobs/listener/BucketMob.java b/src/main/java/simplexity/simplebucketmobs/listener/BucketMob.java index f6cb8de..78d7242 100644 --- a/src/main/java/simplexity/simplebucketmobs/listener/BucketMob.java +++ b/src/main/java/simplexity/simplebucketmobs/listener/BucketMob.java @@ -1,252 +1,128 @@ package simplexity.simplebucketmobs.listener; -import com.mojang.brigadier.exceptions.CommandSyntaxException; +import io.papermc.paper.datacomponent.DataComponentTypes; +import io.papermc.paper.persistence.PersistentDataContainerView; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.Tag; -import net.minecraft.nbt.TagParser; -import org.bukkit.GameMode; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.NamespacedKey; -import org.bukkit.Sound; +import org.bukkit.World; import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.craftbukkit.entity.CraftLivingEntity; -import org.bukkit.entity.ComplexEntityPart; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Monster; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import org.bukkit.event.block.Action; -import org.bukkit.event.entity.CreatureSpawnEvent; -import org.bukkit.event.player.PlayerBucketFillEvent; import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; import org.bukkit.util.BoundingBox; import simplexity.simplebucketmobs.SimpleBucketMobs; import simplexity.simplebucketmobs.config.Config; -import simplexity.simplebucketmobs.config.Texture; -import simplexity.simplebucketmobs.util.Message; import simplexity.simplebucketmobs.util.Permission; -import java.io.IOException; - public class BucketMob implements Listener { - public static final NamespacedKey mobNBTKey = new NamespacedKey(SimpleBucketMobs.getPlugin(), "mob_nbt"); - - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) - public void bucketMob(PlayerInteractEntityEvent event) { - if (event.getHand() != EquipmentSlot.HAND) return; - Player player = event.getPlayer(); - if (!player.isSneaking()) return; - Entity clicked = event.getRightClicked(); - if (clicked instanceof ComplexEntityPart part) clicked = part.getParent(); - if (!(clicked instanceof LivingEntity entity)) return; - if (entity.getType() == EntityType.PLAYER) return; - if (Config.getInstance().isNoHostileTargeting() - && entity instanceof Monster monster - && monster.getTarget() != null - && monster.getTarget().equals(player)) { - player.sendMessage(Message.ERROR_BUCKET_HOSTILE_TARGETING.getParsedMessage()); - return; - } - if (!Config.getInstance().getAllowedBasicTypes().contains(entity.getType())) return; - if (!(player.hasPermission(Permission.BUCKET_ALL.get()) || player.hasPermission(Permission.BUCKET_MOB.get() + entity.getType()))) { - player.sendMessage(Message.ERROR_BUCKET_NO_PERMISSION.getParsedMessage()); - return; - } - // TODO: Health Threshold Requirement / Health Check Bypass Permission (Per Mob) - // TODO: Check disallowed attributes. - ItemStack bucket = player.getEquipment().getItemInMainHand(); - // TODO: Allow for different bucket types. - if (bucket.getType() != Material.BUCKET) return; - if (bucket.getItemMeta().getPersistentDataContainer().has(mobNBTKey)) { - event.setCancelled(true); - return; - } - ItemStack mobBucket = new ItemStack(Material.BUCKET); - Entity vehicle = entity.getVehicle(); - if (vehicle != null) vehicle.removePassenger(entity); - CompoundTag tag = serializeNBT(entity); - String serializedNbt = tag.getAsString(); - - ItemMeta meta = mobBucket.getItemMeta(); - PersistentDataContainer bucketPDC = meta.getPersistentDataContainer(); - bucketPDC.set(mobNBTKey, PersistentDataType.STRING, serializedNbt); - - String entityType = entity.getType().toString(); - String entityTypeName = nameCase(entityType); - Component customName = entity.customName(); - Component entityTypeComponent = SimpleBucketMobs.getPlainTextSerializer().deserialize(entityTypeName); - meta.displayName(SimpleBucketMobs.getMiniMessage().deserialize( - "" + Config.getInstance().getBucketTitle(), - Placeholder.parsed("type", entityType), - Placeholder.parsed("type_name_cased", entityTypeName), - Placeholder.component("display_name", customName != null ? customName : entityTypeComponent) - )); - if (Config.getInstance().isUseResourcePack()) { - Texture.getInstance().setCustomData(entity.getType(), meta, tag); - } else { - meta.setEnchantmentGlintOverride(true); - } - mobBucket.setItemMeta(meta); - if (player.getGameMode() != GameMode.CREATIVE) bucket.subtract(); - if (player.getInventory().firstEmpty() == -1) { - player.getWorld().dropItemNaturally(player.getLocation(), mobBucket); - } else { - player.getInventory().addItem(mobBucket); - } - try { - // TODO: Configurable? - String soundName = "ENTITY_" + entity.getType() + "_HURT"; - player.playSound(player.getLocation(), Sound.valueOf(soundName), 0.75f, 1.0f); - } catch (IllegalArgumentException ignored) { - } - entity.remove(); + public static final NamespacedKey legacyMobTag = new NamespacedKey(SimpleBucketMobs.getPlugin(), "mob_nbt"); + public static final NamespacedKey newMobTag = new NamespacedKey(SimpleBucketMobs.getPlugin(), "serialized_mob_data"); + + @EventHandler + public void onBucketMob(PlayerInteractEntityEvent interactEvent) { + Entity entity = interactEvent.getRightClicked(); + Player player = interactEvent.getPlayer(); + if (!(entity instanceof LivingEntity livingEntity)) return; + EntityType type = livingEntity.getType(); + if (!Config.getInstance().getAllowedBasicTypes().contains(type)) return; + if (!(player.hasPermission(Permission.BUCKET_MOB.get() + type.toString().toLowerCase()) + || player.hasPermission(Permission.BUCKET_ALL.get()))) return; + if (player.isSneaking()) return; + ItemStack itemInHand = player.getInventory().getItemInMainHand(); + if (!itemInHand.getType().equals(Material.BUCKET)) return; + PersistentDataContainerView bucketPdcView = itemInHand.getPersistentDataContainer(); + if (bucketPdcView.has(legacyMobTag)) return; + if (bucketPdcView.has(newMobTag)) return; + byte[] serializedEntity = Bukkit.getUnsafe().serializeEntity(livingEntity); + ItemStack newBucket = ItemStack.of(Material.BUCKET); + newBucket.editPersistentDataContainer(pdc -> pdc.set(newMobTag, PersistentDataType.BYTE_ARRAY, serializedEntity)); + newBucket.setData(DataComponentTypes.CUSTOM_NAME, Component.text(type + " in a bucket")); + player.getInventory().setItemInMainHand(newBucket); + livingEntity.remove(); + interactEvent.setCancelled(true); } - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) - public void unbucketMob(PlayerInteractEvent event) { - if (event.getHand() != EquipmentSlot.HAND) return; - if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; - Block block = event.getClickedBlock(); - if (block == null) return; - Location location = block.getRelative(event.getBlockFace()).getLocation().add(0.5, 0.0, 0.5); - - Player player = event.getPlayer(); - ItemStack bucket = player.getEquipment().getItemInMainHand(); - if (bucket.getType() != Material.BUCKET) return; - if (!bucket.getItemMeta().getPersistentDataContainer().has(mobNBTKey)) return; - - String serializedNbt = bucket.getItemMeta().getPersistentDataContainer().get(mobNBTKey, PersistentDataType.STRING); - - try { - if (serializedNbt != null) applyNBT(location, serializedNbt, event.getBlockFace()); - } catch (IOException | CommandSyntaxException e) { - player.sendMessage(Message.ERROR_FAILED_DESERIALIZATION.getParsedMessage()); - e.printStackTrace(); + @EventHandler(ignoreCancelled = true) + public void onUnbucketMob(PlayerInteractEvent interactEvent) { + ItemStack itemStack = interactEvent.getPlayer().getInventory().getItemInMainHand(); + if (interactEvent.getAction().isLeftClick()) return; + if (!interactEvent.hasBlock()) return; + Block block = interactEvent.getClickedBlock(); + if (block == null || block.getType().equals(Material.AIR)) return; + if (!itemStack.getType().equals(Material.BUCKET)) return; + PersistentDataContainerView bucketPdc = itemStack.getPersistentDataContainer(); + if (!bucketPdc.has(newMobTag)) return; + byte[] entityByteArray = bucketPdc.get(newMobTag, PersistentDataType.BYTE_ARRAY); + if (entityByteArray == null) return; + Entity deserializedEntity = Bukkit.getUnsafe().deserializeEntity(entityByteArray, block.getWorld(), true, false); + if (!(deserializedEntity instanceof LivingEntity livingEntity)) return; + Player player = interactEvent.getPlayer(); + if (!(player.hasPermission(Permission.BUCKET_MOB.get() + deserializedEntity.getType()) || player.hasPermission(Permission.BUCKET_ALL.get()))) return; - } catch (IllegalArgumentException e) { - player.sendMessage(Message.ERROR_NO_BUCKET_MOB.getParsedMessage()); - e.printStackTrace(); - return; - } - - // TODO: Make configurable. - player.playSound(player.getLocation(), Sound.ITEM_BUCKET_FILL_POWDER_SNOW, 1.0f, 1.0f); - - if (player.getGameMode() != GameMode.CREATIVE) { - bucket.subtract(); - player.getInventory().addItem(new ItemStack(Material.BUCKET)); + Location clickedLocation = block.getLocation(); + Location summonPoint = findSafeSummonPoint(clickedLocation.getWorld(), clickedLocation, deserializedEntity.getBoundingBox(), 5); + if (summonPoint == null) { + summonPoint = block.getLocation().toCenterLocation().add(0, 1, 0); } + interactEvent.setCancelled(true); + livingEntity.teleport(summonPoint); + summonPoint.getWorld().addEntity(livingEntity); + itemStack.editPersistentDataContainer(pdc -> pdc.remove(newMobTag)); + itemStack.setData(DataComponentTypes.CUSTOM_NAME, Component.translatable("item.minecraft.bucket").decoration(TextDecoration.ITALIC, false)); } - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) - public void noBucketLiquid(PlayerBucketFillEvent event) { - Player player = event.getPlayer(); - ItemStack bucket = player.getEquipment().getItem(event.getHand()); - if (bucket.getItemMeta().getPersistentDataContainer().has(mobNBTKey)) { - event.setCancelled(true); - } - // TODO: Make it so that when performing bucketMob and unbucketMob, the bucket does not collect liquid. - } - /** - * Serializes the NBT Data from the LivingEntity. - * - * @param e LivingEntity - * @return String serialization of the LivingEntity. - */ - private CompoundTag serializeNBT(LivingEntity e) { - CompoundTag tag = new CompoundTag(); - ((CraftLivingEntity) e).getHandle().save(tag); - return tag; - } + public static Location findSafeSummonPoint(World world, Location center, BoundingBox boundingBox, int radius) { + double width = boundingBox.getWidthX(); + double depth = boundingBox.getWidthZ(); + double height = boundingBox.getHeight(); - /** - * Deserializes the NBT Data into the LivingEntity. - * - * @param location Location to spawn Mob. - * @param serializedNbt NBT as a String - * @throws IllegalArgumentException Invalid Mob Type Found - * @throws IOException Failed to read NBT Tags. - * @throws CommandSyntaxException What. - */ - private void applyNBT(Location location, String serializedNbt, BlockFace face) throws IllegalArgumentException, IOException, CommandSyntaxException { - CompoundTag tag = TagParser.parseTag(serializedNbt); - Tag idTag = tag.get("id"); - // TODO: Maybe throw exception. - if (idTag == null) return; - String id = idTag.getAsString().split(":")[1].toUpperCase(); - EntityType mobType = EntityType.valueOf(id); - Entity entity = location.getWorld().spawnEntity(location, mobType, CreatureSpawnEvent.SpawnReason.CUSTOM, spawned -> { - CompoundTag newLoc = new CompoundTag(); - ((CraftLivingEntity) spawned).getHandle().save(newLoc); - tag.put("Motion", newLoc.get("Motion")); - tag.put("Pos", newLoc.get("Pos")); - tag.put("Rotation", newLoc.get("Rotation")); - tag.put("UUID", newLoc.get("UUID")); - ((CraftLivingEntity) spawned).getHandle().load(tag); - }); - // TODO: Adjust Entity on Bounding Box -- entity.teleport(adjustLoc(location, face, entity.getBoundingBox())); - } + for (int distY = -2; distY <= 2; distY++) { + for (int distX = -radius; distX <= radius; distX++) { + for (int distZ = -radius; distZ <= radius; distZ++) { + Location startingLocation = new Location(world, center.getX() + distX, distY, center.getZ() + distZ); + if (isAreaClear(world, startingLocation, width, height, depth)) { + return startingLocation.add(0.5, 0, 0.5); + } - /** - * Converts a string to Name Case - * - * @param input String - * @return String but using Name Case - * @implNote Thanks Baeldung (https://www.baeldung.com/java-string-title-case) - */ - private String nameCase(String input) { - if (input == null || input.isBlank()) return input; - input = input.replace('_', ' '); - StringBuilder nameCased = new StringBuilder(); - boolean toUpper = true; - for (char c : input.toCharArray()) { - if (Character.isSpaceChar(c)) toUpper = true; - else if (toUpper) { - c = Character.toTitleCase(c); - toUpper = false; - } else { - c = Character.toLowerCase(c); + } } - nameCased.append(c); } - return nameCased.toString(); + return null; } - /** - * Adjusts the location given - * - * @param interactionPoint Location of Interaction Point - * @param face Block Face of Interaction - * @return The same Location. - */ - private Location adjustLoc(Location interactionPoint, BlockFace face, BoundingBox entityBox) { - double height = entityBox.getHeight(); - double widthX = entityBox.getWidthX(); - double widthZ = entityBox.getWidthZ(); - switch (face) { - case DOWN -> interactionPoint.add(0, -1 * height, 0); - case EAST -> interactionPoint.add(.5 * widthX, 0, 0); - case WEST -> interactionPoint.add(-.5 * widthX, 0, 0); - case NORTH -> interactionPoint.add(0, 0, -.5 * widthZ); - case SOUTH -> interactionPoint.add(0, 0, .5 * widthZ); + public static boolean isAreaClear(World world, Location startLoc, double width, double height, double depth) { + int minX = (int) Math.floor(startLoc.getX() - (width / 2)); + int maxX = (int) Math.ceil(startLoc.getX() + (width / 2)); + int minY = startLoc.getBlockY(); + int maxY = (int) Math.ceil(startLoc.getY() + height); + int minZ = (int) Math.floor(startLoc.getZ() - (depth / 2)); + int maxZ = (int) Math.ceil(startLoc.getZ() + (depth / 2)); + + Block floor = world.getBlockAt(startLoc.getBlockX(), startLoc.getBlockY() - 1, startLoc.getBlockZ()); + if (!floor.getType().isSolid()) return false; + + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + for (int z = minZ; z <= maxZ; z++) { + Block block = world.getBlockAt(x, y, z); + if (!block.isPassable()) return false; + } + } } - return interactionPoint; + return true; } } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 822cdfd..a01c592 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -2,7 +2,7 @@ name: SimpleBucketMobs group: simplexity version: "${version}" main: simplexity.simplebucketmobs.SimpleBucketMobs -api-version: "1.20" +api-version: "1.21.6" authors: [ Rhythmic, Peashooter101 ] description: Shove your mobs into buckets like Axolotls! :D commands: @@ -20,4 +20,4 @@ permissions: description: "Reload plugin configuration files." default: op simplebucketmobs.debucket: - description: "Dump saved mob NBT data from Mob Bucket to chat." \ No newline at end of file + description: "Dump saved mob NBT data from Mob Bucket to chat." From ca603f7acacdcf33ecc14d1e5b6828c28cf88542 Mon Sep 17 00:00:00 2001 From: Rhythmic System Date: Tue, 22 Jul 2025 15:20:09 -0700 Subject: [PATCH 03/11] Old mob buckets can be used --- .../listener/BucketHandler.java | 50 +++++++++++++ .../simplebucketmobs/listener/BucketMob.java | 73 ++++++------------- 2 files changed, 74 insertions(+), 49 deletions(-) create mode 100644 src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java diff --git a/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java b/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java new file mode 100644 index 0000000..30a79f9 --- /dev/null +++ b/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java @@ -0,0 +1,50 @@ +package simplexity.simplebucketmobs.listener; + +import io.papermc.paper.datacomponent.DataComponentTypes; +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataType; +import simplexity.simplebucketmobs.SimpleBucketMobs; + +import java.util.HashMap; + +public class BucketHandler { + + public static ItemStack getMobBucket(LivingEntity entity) { + ItemStack bucketStack = new ItemStack(Material.BUCKET); + bucketStack.setData(DataComponentTypes.MAX_STACK_SIZE, 1); + Component nameComponent = getBucketName(entity); + bucketStack.setData(DataComponentTypes.CUSTOM_NAME, nameComponent); + bucketStack.setData(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, true); + byte[] serializedEntity = Bukkit.getUnsafe().serializeEntity(entity); + bucketStack.editPersistentDataContainer(pdc -> pdc.set(BucketMob.newMobTag, PersistentDataType.BYTE_ARRAY, serializedEntity)); + return bucketStack; + } + + + public static void addBucketToInventory(Player player, ItemStack bucket) { + Inventory inventory = player.getInventory(); + int amountOfBuckets = player.getInventory().getItemInMainHand().getAmount(); + player.getInventory().getItemInMainHand().setAmount(amountOfBuckets - 1); + HashMap leftoverItems = inventory.addItem(bucket); + if (leftoverItems.isEmpty()) return; + for (ItemStack leftover : leftoverItems.values()) { + player.getLocation().getWorld().dropItem(player.getLocation(), leftover); + } + } + + private static Component getBucketName(LivingEntity entity) { + Component nameComponent = Component.empty(); + nameComponent = nameComponent.append(Component.text(entity.getType().toString().toLowerCase())); + + nameComponent = nameComponent.append(Component.text(" Bucket")); + return nameComponent; + } + +} diff --git a/src/main/java/simplexity/simplebucketmobs/listener/BucketMob.java b/src/main/java/simplexity/simplebucketmobs/listener/BucketMob.java index 78d7242..11a6438 100644 --- a/src/main/java/simplexity/simplebucketmobs/listener/BucketMob.java +++ b/src/main/java/simplexity/simplebucketmobs/listener/BucketMob.java @@ -1,9 +1,14 @@ package simplexity.simplebucketmobs.listener; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import io.papermc.paper.datacomponent.DataComponentTypes; import io.papermc.paper.persistence.PersistentDataContainerView; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.TextDecoration; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.TagParser; +import net.minecraft.world.level.storage.ValueInput; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; @@ -11,6 +16,8 @@ import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityFactory; +import org.bukkit.entity.EntitySnapshot; import org.bukkit.entity.EntityType; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; @@ -21,6 +28,7 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataType; import org.bukkit.util.BoundingBox; +import org.checkerframework.checker.units.qual.C; import simplexity.simplebucketmobs.SimpleBucketMobs; import simplexity.simplebucketmobs.config.Config; import simplexity.simplebucketmobs.util.Permission; @@ -45,11 +53,8 @@ public void onBucketMob(PlayerInteractEntityEvent interactEvent) { PersistentDataContainerView bucketPdcView = itemInHand.getPersistentDataContainer(); if (bucketPdcView.has(legacyMobTag)) return; if (bucketPdcView.has(newMobTag)) return; - byte[] serializedEntity = Bukkit.getUnsafe().serializeEntity(livingEntity); - ItemStack newBucket = ItemStack.of(Material.BUCKET); - newBucket.editPersistentDataContainer(pdc -> pdc.set(newMobTag, PersistentDataType.BYTE_ARRAY, serializedEntity)); - newBucket.setData(DataComponentTypes.CUSTOM_NAME, Component.text(type + " in a bucket")); - player.getInventory().setItemInMainHand(newBucket); + ItemStack bucketItem = BucketHandler.getMobBucket(livingEntity); + BucketHandler.addBucketToInventory(player, bucketItem); livingEntity.remove(); interactEvent.setCancelled(true); } @@ -63,6 +68,13 @@ public void onUnbucketMob(PlayerInteractEvent interactEvent) { if (block == null || block.getType().equals(Material.AIR)) return; if (!itemStack.getType().equals(Material.BUCKET)) return; PersistentDataContainerView bucketPdc = itemStack.getPersistentDataContainer(); + if (bucketPdc.has(legacyMobTag)) { + String entityNbt = bucketPdc.get(legacyMobTag, PersistentDataType.STRING); + spawnOldMobs(entityNbt, block.getLocation().toCenterLocation().add(0, 1, 0)); + interactEvent.setCancelled(true); + interactEvent.getPlayer().getInventory().setItemInMainHand(new ItemStack(Material.BUCKET)); + return; + } if (!bucketPdc.has(newMobTag)) return; byte[] entityByteArray = bucketPdc.get(newMobTag, PersistentDataType.BYTE_ARRAY); if (entityByteArray == null) return; @@ -72,57 +84,20 @@ public void onUnbucketMob(PlayerInteractEvent interactEvent) { if (!(player.hasPermission(Permission.BUCKET_MOB.get() + deserializedEntity.getType()) || player.hasPermission(Permission.BUCKET_ALL.get()))) return; Location clickedLocation = block.getLocation(); - Location summonPoint = findSafeSummonPoint(clickedLocation.getWorld(), clickedLocation, deserializedEntity.getBoundingBox(), 5); - if (summonPoint == null) { - summonPoint = block.getLocation().toCenterLocation().add(0, 1, 0); - } + Location summonPoint = block.getLocation().toCenterLocation().add(0, 1, 0); + + if (Bukkit.getEntity(livingEntity.getUniqueId()) != null) livingEntity = (LivingEntity) livingEntity.copy(); interactEvent.setCancelled(true); livingEntity.teleport(summonPoint); summonPoint.getWorld().addEntity(livingEntity); - itemStack.editPersistentDataContainer(pdc -> pdc.remove(newMobTag)); - itemStack.setData(DataComponentTypes.CUSTOM_NAME, Component.translatable("item.minecraft.bucket").decoration(TextDecoration.ITALIC, false)); + player.getInventory().setItemInMainHand(new ItemStack(Material.BUCKET)); } - public static Location findSafeSummonPoint(World world, Location center, BoundingBox boundingBox, int radius) { - double width = boundingBox.getWidthX(); - double depth = boundingBox.getWidthZ(); - double height = boundingBox.getHeight(); - - for (int distY = -2; distY <= 2; distY++) { - for (int distX = -radius; distX <= radius; distX++) { - for (int distZ = -radius; distZ <= radius; distZ++) { - Location startingLocation = new Location(world, center.getX() + distX, distY, center.getZ() + distZ); - if (isAreaClear(world, startingLocation, width, height, depth)) { - return startingLocation.add(0.5, 0, 0.5); - } - - } - } - } - return null; + private void spawnOldMobs(String nbt, Location location) { + EntitySnapshot entitySnapshot = Bukkit.getEntityFactory().createEntitySnapshot(nbt); + entitySnapshot.createEntity(location); } - public static boolean isAreaClear(World world, Location startLoc, double width, double height, double depth) { - int minX = (int) Math.floor(startLoc.getX() - (width / 2)); - int maxX = (int) Math.ceil(startLoc.getX() + (width / 2)); - int minY = startLoc.getBlockY(); - int maxY = (int) Math.ceil(startLoc.getY() + height); - int minZ = (int) Math.floor(startLoc.getZ() - (depth / 2)); - int maxZ = (int) Math.ceil(startLoc.getZ() + (depth / 2)); - - Block floor = world.getBlockAt(startLoc.getBlockX(), startLoc.getBlockY() - 1, startLoc.getBlockZ()); - if (!floor.getType().isSolid()) return false; - - for (int x = minX; x <= maxX; x++) { - for (int y = minY; y <= maxY; y++) { - for (int z = minZ; z <= maxZ; z++) { - Block block = world.getBlockAt(x, y, z); - if (!block.isPassable()) return false; - } - } - } - return true; - } } From 536ed56de841eb0f2bc0c6ee40131537374db497 Mon Sep 17 00:00:00 2001 From: Rhythmic System Date: Tue, 22 Jul 2025 16:00:14 -0700 Subject: [PATCH 04/11] Some cleanup --- .../simplebucketmobs/command/SubCommand.java | 6 +- .../command/subcommand/Debucket.java | 6 +- .../command/subcommand/Reload.java | 6 +- .../listener/BucketHandler.java | 5 +- .../simplebucketmobs/listener/BucketMob.java | 65 ++++--------------- .../listener/EntityHandler.java | 45 +++++++++++++ .../util/BucketMobPermission.java | 13 ++++ .../simplebucketmobs/util/Permission.java | 19 ------ 8 files changed, 82 insertions(+), 83 deletions(-) create mode 100644 src/main/java/simplexity/simplebucketmobs/listener/EntityHandler.java create mode 100644 src/main/java/simplexity/simplebucketmobs/util/BucketMobPermission.java delete mode 100644 src/main/java/simplexity/simplebucketmobs/util/Permission.java diff --git a/src/main/java/simplexity/simplebucketmobs/command/SubCommand.java b/src/main/java/simplexity/simplebucketmobs/command/SubCommand.java index 6ee277f..48525c3 100644 --- a/src/main/java/simplexity/simplebucketmobs/command/SubCommand.java +++ b/src/main/java/simplexity/simplebucketmobs/command/SubCommand.java @@ -1,7 +1,7 @@ package simplexity.simplebucketmobs.command; -import simplexity.simplebucketmobs.util.Permission; import org.bukkit.command.CommandSender; +import org.bukkit.permissions.Permission; import java.util.List; @@ -30,7 +30,9 @@ public String getSyntax() { return syntax; } - public String getPermission() { return permission.get(); } + public Permission getPermission() { + return permission; + } public abstract void execute(CommandSender sender, String[] args); diff --git a/src/main/java/simplexity/simplebucketmobs/command/subcommand/Debucket.java b/src/main/java/simplexity/simplebucketmobs/command/subcommand/Debucket.java index 38000db..8c5f193 100644 --- a/src/main/java/simplexity/simplebucketmobs/command/subcommand/Debucket.java +++ b/src/main/java/simplexity/simplebucketmobs/command/subcommand/Debucket.java @@ -3,7 +3,7 @@ import simplexity.simplebucketmobs.command.SubCommand; import simplexity.simplebucketmobs.listener.BucketMob; import simplexity.simplebucketmobs.util.Message; -import simplexity.simplebucketmobs.util.Permission; +import simplexity.simplebucketmobs.util.BucketMobPermission; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.inventory.EquipmentSlot; @@ -15,7 +15,7 @@ public class Debucket extends SubCommand { public Debucket() { - super("debucket", "Allows you to dump the JSON NBT data to the player chat.", "/sbm debucket", Permission.COMMAND_DEBUCKET); + super("debucket", "Allows you to dump the JSON NBT data to the player chat.", "/sbm debucket", BucketMobPermission.DEBUCKET_COMMAND); } @Override @@ -24,7 +24,7 @@ public void execute(CommandSender sender, String[] args) { sender.sendMessage(Message.ERROR_NOT_A_PLAYER.getParsedMessage()); return; } - if (!sender.hasPermission(Permission.COMMAND_DEBUCKET.get())) { + if (!sender.hasPermission(BucketMobPermission.DEBUCKET_COMMAND)) { sender.sendMessage(Message.ERROR_COMMAND_NO_PERMISSION.getParsedMessage()); return; } diff --git a/src/main/java/simplexity/simplebucketmobs/command/subcommand/Reload.java b/src/main/java/simplexity/simplebucketmobs/command/subcommand/Reload.java index 4a876c2..87d6cea 100644 --- a/src/main/java/simplexity/simplebucketmobs/command/subcommand/Reload.java +++ b/src/main/java/simplexity/simplebucketmobs/command/subcommand/Reload.java @@ -4,19 +4,19 @@ import simplexity.simplebucketmobs.command.CommandHandler; import simplexity.simplebucketmobs.command.SubCommand; import simplexity.simplebucketmobs.util.Message; -import simplexity.simplebucketmobs.util.Permission; +import simplexity.simplebucketmobs.util.BucketMobPermission; import org.bukkit.command.CommandSender; import java.util.List; public class Reload extends SubCommand { public Reload() { - super("reload", "Reloads SimpleBucketMobs", "/sbm reload", Permission.COMMAND_RELOAD); + super("reload", "Reloads SimpleBucketMobs", "/sbm reload", BucketMobPermission.COMMAND_RELOAD); } @Override public void execute(CommandSender sender, String[] args) { - if (!sender.hasPermission(Permission.COMMAND_RELOAD.get())) { + if (!sender.hasPermission(BucketMobPermission.COMMAND_RELOAD)) { sender.sendMessage(Message.ERROR_COMMAND_NO_PERMISSION.getParsedMessage()); return; } diff --git a/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java b/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java index 30a79f9..d05a74a 100644 --- a/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java +++ b/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java @@ -4,16 +4,15 @@ import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; import org.bukkit.Material; -import org.bukkit.NamespacedKey; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataType; -import simplexity.simplebucketmobs.SimpleBucketMobs; import java.util.HashMap; +@SuppressWarnings({"UnstableApiUsage", "deprecation"}) public class BucketHandler { public static ItemStack getMobBucket(LivingEntity entity) { @@ -28,7 +27,7 @@ public static ItemStack getMobBucket(LivingEntity entity) { } - public static void addBucketToInventory(Player player, ItemStack bucket) { + public static void addMobBucketToInventory(Player player, ItemStack bucket) { Inventory inventory = player.getInventory(); int amountOfBuckets = player.getInventory().getItemInMainHand().getAmount(); player.getInventory().getItemInMainHand().setAmount(amountOfBuckets - 1); diff --git a/src/main/java/simplexity/simplebucketmobs/listener/BucketMob.java b/src/main/java/simplexity/simplebucketmobs/listener/BucketMob.java index 11a6438..6be54c6 100644 --- a/src/main/java/simplexity/simplebucketmobs/listener/BucketMob.java +++ b/src/main/java/simplexity/simplebucketmobs/listener/BucketMob.java @@ -1,23 +1,10 @@ package simplexity.simplebucketmobs.listener; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import io.papermc.paper.datacomponent.DataComponentTypes; import io.papermc.paper.persistence.PersistentDataContainerView; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.TextDecoration; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtIo; -import net.minecraft.nbt.TagParser; -import net.minecraft.world.level.storage.ValueInput; -import org.bukkit.Bukkit; -import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.NamespacedKey; -import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityFactory; -import org.bukkit.entity.EntitySnapshot; import org.bukkit.entity.EntityType; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; @@ -26,12 +13,9 @@ import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.ItemStack; -import org.bukkit.persistence.PersistentDataType; -import org.bukkit.util.BoundingBox; -import org.checkerframework.checker.units.qual.C; import simplexity.simplebucketmobs.SimpleBucketMobs; import simplexity.simplebucketmobs.config.Config; -import simplexity.simplebucketmobs.util.Permission; +import simplexity.simplebucketmobs.util.BucketMobPermission; public class BucketMob implements Listener { @@ -45,58 +29,33 @@ public void onBucketMob(PlayerInteractEntityEvent interactEvent) { if (!(entity instanceof LivingEntity livingEntity)) return; EntityType type = livingEntity.getType(); if (!Config.getInstance().getAllowedBasicTypes().contains(type)) return; - if (!(player.hasPermission(Permission.BUCKET_MOB.get() + type.toString().toLowerCase()) - || player.hasPermission(Permission.BUCKET_ALL.get()))) return; + if (!(player.hasPermission(BucketMobPermission.BUCKET_MOB_BASE + type.toString().toLowerCase()) || player.hasPermission(BucketMobPermission.BUCKET_ALL))) + return; if (player.isSneaking()) return; ItemStack itemInHand = player.getInventory().getItemInMainHand(); if (!itemInHand.getType().equals(Material.BUCKET)) return; PersistentDataContainerView bucketPdcView = itemInHand.getPersistentDataContainer(); - if (bucketPdcView.has(legacyMobTag)) return; - if (bucketPdcView.has(newMobTag)) return; + if (bucketPdcView.has(legacyMobTag) || bucketPdcView.has(newMobTag)) return; ItemStack bucketItem = BucketHandler.getMobBucket(livingEntity); - BucketHandler.addBucketToInventory(player, bucketItem); + BucketHandler.addMobBucketToInventory(player, bucketItem); livingEntity.remove(); interactEvent.setCancelled(true); } @EventHandler(ignoreCancelled = true) public void onUnbucketMob(PlayerInteractEvent interactEvent) { - ItemStack itemStack = interactEvent.getPlayer().getInventory().getItemInMainHand(); + Player player = interactEvent.getPlayer(); + ItemStack itemStack = player.getInventory().getItemInMainHand(); + PersistentDataContainerView bucketPdc = itemStack.getPersistentDataContainer(); + Block block = interactEvent.getClickedBlock(); if (interactEvent.getAction().isLeftClick()) return; if (!interactEvent.hasBlock()) return; - Block block = interactEvent.getClickedBlock(); if (block == null || block.getType().equals(Material.AIR)) return; if (!itemStack.getType().equals(Material.BUCKET)) return; - PersistentDataContainerView bucketPdc = itemStack.getPersistentDataContainer(); - if (bucketPdc.has(legacyMobTag)) { - String entityNbt = bucketPdc.get(legacyMobTag, PersistentDataType.STRING); - spawnOldMobs(entityNbt, block.getLocation().toCenterLocation().add(0, 1, 0)); - interactEvent.setCancelled(true); - interactEvent.getPlayer().getInventory().setItemInMainHand(new ItemStack(Material.BUCKET)); - return; - } - if (!bucketPdc.has(newMobTag)) return; - byte[] entityByteArray = bucketPdc.get(newMobTag, PersistentDataType.BYTE_ARRAY); - if (entityByteArray == null) return; - Entity deserializedEntity = Bukkit.getUnsafe().deserializeEntity(entityByteArray, block.getWorld(), true, false); - if (!(deserializedEntity instanceof LivingEntity livingEntity)) return; - Player player = interactEvent.getPlayer(); - if (!(player.hasPermission(Permission.BUCKET_MOB.get() + deserializedEntity.getType()) || player.hasPermission(Permission.BUCKET_ALL.get()))) - return; - Location clickedLocation = block.getLocation(); - Location summonPoint = block.getLocation().toCenterLocation().add(0, 1, 0); - - if (Bukkit.getEntity(livingEntity.getUniqueId()) != null) livingEntity = (LivingEntity) livingEntity.copy(); + if (!(bucketPdc.has(newMobTag) || bucketPdc.has(legacyMobTag))) return; interactEvent.setCancelled(true); - livingEntity.teleport(summonPoint); - summonPoint.getWorld().addEntity(livingEntity); - player.getInventory().setItemInMainHand(new ItemStack(Material.BUCKET)); - } - - - private void spawnOldMobs(String nbt, Location location) { - EntitySnapshot entitySnapshot = Bukkit.getEntityFactory().createEntitySnapshot(nbt); - entitySnapshot.createEntity(location); + if (bucketPdc.has(newMobTag)) EntityHandler.handleMobSpawn(player, itemStack, block); + if (bucketPdc.has(legacyMobTag)) EntityHandler.handleLegacyMobSpawn(player, itemStack, block); } diff --git a/src/main/java/simplexity/simplebucketmobs/listener/EntityHandler.java b/src/main/java/simplexity/simplebucketmobs/listener/EntityHandler.java new file mode 100644 index 0000000..2ccf6c1 --- /dev/null +++ b/src/main/java/simplexity/simplebucketmobs/listener/EntityHandler.java @@ -0,0 +1,45 @@ +package simplexity.simplebucketmobs.listener; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntitySnapshot; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataType; +import org.slf4j.Logger; +import simplexity.simplebucketmobs.SimpleBucketMobs; + +@SuppressWarnings({"UnstableApiUsage", "deprecation"}) +public class EntityHandler { + + private static final Logger logger = SimpleBucketMobs.getPlugin().getSLF4JLogger(); + + public static void handleMobSpawn(Player player, ItemStack itemStack, Block block){ + byte[] entityBytes = itemStack.getPersistentDataContainer().get(BucketMob.newMobTag, PersistentDataType.BYTE_ARRAY); + if (entityBytes == null) return; + Entity deserializedEntity = Bukkit.getUnsafe().deserializeEntity(entityBytes, block.getWorld(), false, false); + if (!(deserializedEntity instanceof LivingEntity livingEntity)) { + logger.warn("Entity saved in this bucket was not a living entity, idk how you managed to do that but it's not gonna spawn. Entity info: {}", deserializedEntity); + return; + } + Location location = block.getLocation().toCenterLocation(); + if (!block.isPassable()) location = location.add(0, 1, 0); + livingEntity.teleport(location); + location.getWorld().addEntity(livingEntity); + player.getInventory().setItemInMainHand(new ItemStack(Material.BUCKET)); + } + + public static void handleLegacyMobSpawn(Player player, ItemStack itemStack, Block block){ + String nbt = itemStack.getPersistentDataContainer().get(BucketMob.legacyMobTag, PersistentDataType.STRING); + if (nbt == null || nbt.isEmpty()) return; + Location location = block.getLocation().toCenterLocation(); + if (!block.isPassable()) location = location.add(0, 1, 0); + EntitySnapshot entitySnapshot = Bukkit.getEntityFactory().createEntitySnapshot(nbt); + entitySnapshot.createEntity(location); + player.getInventory().setItemInMainHand(new ItemStack(Material.BUCKET)); + } +} diff --git a/src/main/java/simplexity/simplebucketmobs/util/BucketMobPermission.java b/src/main/java/simplexity/simplebucketmobs/util/BucketMobPermission.java new file mode 100644 index 0000000..0176608 --- /dev/null +++ b/src/main/java/simplexity/simplebucketmobs/util/BucketMobPermission.java @@ -0,0 +1,13 @@ +package simplexity.simplebucketmobs.util; + +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; + +public class BucketMobPermission { + + public static final Permission DEBUCKET_COMMAND = new Permission("simplebucketmobs.debucket", "Dump saved mob NBT data from Mob Bucket to chat.", PermissionDefault.OP); + public static final Permission COMMAND_RELOAD = new Permission("simplebucketmobs.reload", "Reload plugin configuration files.", PermissionDefault.OP); + + public static final Permission BUCKET_MOB_BASE = new Permission("simplebucketmobs.bucket.", "Allows the user to bucket the specified mob.", PermissionDefault.OP); + public static final Permission BUCKET_ALL = new Permission("simplebucketmobs.bucket.all", "Allows the user to bucket all enabled mobs.", PermissionDefault.TRUE); +} diff --git a/src/main/java/simplexity/simplebucketmobs/util/Permission.java b/src/main/java/simplexity/simplebucketmobs/util/Permission.java deleted file mode 100644 index 6b29aa1..0000000 --- a/src/main/java/simplexity/simplebucketmobs/util/Permission.java +++ /dev/null @@ -1,19 +0,0 @@ -package simplexity.simplebucketmobs.util; - -public enum Permission { - - COMMAND_DEBUCKET("simplebucketmobs.debucket"), - COMMAND_RELOAD("simplebucketmobs.reload"), - - BUCKET_MOB("simplebucketmobs.bucket."), - BUCKET_ALL("simplebucketmobs.bucket.all"); - - final String permission; - - Permission(String permission) { - this.permission = permission; - } - - public String get() { return this.permission; } - -} From 0a70a699e976355b1f52969c2c5c5766c2e2f57b Mon Sep 17 00:00:00 2001 From: Rhythmic System Date: Tue, 22 Jul 2025 16:23:36 -0700 Subject: [PATCH 05/11] Config options should work again --- build.gradle.kts | 2 +- .../simplebucketmobs/config/Texture.java | 102 +++--------------- .../listener/BucketHandler.java | 51 ++++++++- .../simplebucketmobs/listener/BucketMob.java | 13 ++- .../util/BucketMobPermission.java | 2 +- src/main/resources/plugin.yml | 1 + 6 files changed, 74 insertions(+), 97 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index c7f4685..2e044b7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,4 @@ -version = "1.2.1" +version = "1.3.0" plugins { `java-library` diff --git a/src/main/java/simplexity/simplebucketmobs/config/Texture.java b/src/main/java/simplexity/simplebucketmobs/config/Texture.java index 09cb16d..b21f467 100644 --- a/src/main/java/simplexity/simplebucketmobs/config/Texture.java +++ b/src/main/java/simplexity/simplebucketmobs/config/Texture.java @@ -1,15 +1,10 @@ package simplexity.simplebucketmobs.config; -import org.bukkit.NamespacedKey; -import simplexity.simplebucketmobs.SimpleBucketMobs; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.Tag; -import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.EntityType; -import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.entity.LivingEntity; +import simplexity.simplebucketmobs.SimpleBucketMobs; import java.io.File; import java.io.IOException; @@ -32,91 +27,20 @@ public static Texture getInstance() { return instance; } - public FileConfiguration getTextureConfig() { return texture; } + public FileConfiguration getTextureConfig() { + return texture; + } public void reloadTextureConfig() { - try { texture.load(dataFile); } - catch (IOException | InvalidConfigurationException e) { e.printStackTrace(); } + try { + texture.load(dataFile); + } catch (IOException | InvalidConfigurationException e) { + e.printStackTrace(); + } } -// public void setCustomData(EntityType type, ItemMeta meta, CompoundTag tag) { -// ConfigurationSection section = texture.getConfigurationSection(type.toString()); -// if (section == null) return; -// setNewItemModel(meta, section); -// if (surprise(type, meta, tag)) return; -// String value = null; -// // Until we hit a dead end or found the path... -// while (true) { -// String ymlKey = null; -// // Find a valid key to look into. -// for (String key : section.getKeys(false)) { -// // If the tag does not exist, ignore this value. -// if (!tag.contains(key)) continue; -// ymlKey = key; -// break; -// } -// if (ymlKey == null) break; -// // If the key does not lead to a configuration section, we cannot continue. -// if (!section.isConfigurationSection(ymlKey)) { -// break; -// } -// // If the tag is not a CompoundTag, this is the value we need. -// // TODO: Make it so if a value "exists" it is true, not just that the value is set to a specific one. -// if (!tag.contains(ymlKey, 10)) { -// Tag currentTag = tag.get(ymlKey); -// assert currentTag != null; // Guaranteed, we checked. -// value = currentTag.getAsString(); -// section = section.getConfigurationSection(ymlKey); -// break; -// } -// // Otherwise, go deeper... -// section = section.getConfigurationSection(ymlKey); -// tag = tag.getCompound(ymlKey); -// assert section != null; // Guaranteed, ymlKey was pulled out of the keySet and the set was unmodified. -// } -// if (value == null) return; -// assert section != null; // Guaranteed, ymlKey was pulled out of the keySet and the set was unmodified. -// setNewItemModel(value, meta, section); -// } -// -// private boolean surprise(EntityType type, ItemMeta meta, CompoundTag tag) { -// ConfigurationSection section = texture.getConfigurationSection("special"); -// if (section == null) return false; -// if (type == EntityType.CHICKEN && tag.getShort("Fire") > 0) { -// setNewItemModel("fried", meta, section); -// return true; -// } -// // Dog having owner workaround. TODO: Be smarter and figure a solution. -// /*if (type == EntityType.WOLF && tag.hasUUID("Owner")) { -// meta.setCustomModelData(section.getInt("tamed_wolf", 0)); -// return true; -// } -// // Toast Rabbit workaround. -// TODO: Fix Toast Rabbit Workaround or be smarter and figure a solution. -// if (type == EntityType.RABBIT) { -// System.out.println("RABBIT"); -// Tag name = tag.get("CustomName"); -// if (name != null) { -// System.out.println("RABBIT NAME"); -// Component nameComponent = SimpleBucketMobs.getGsonSerializer().deserialize(name.toString()); -// if (SimpleBucketMobs.getMiniMessage().stripTags(nameString).equals("Toast")) { -// System.out.println("RABBIT NAME TOAST"); -// meta.setCustomModelData(section.getInt("toast", 0)); -// return true; -// } -// } -// } -// */ -// return false; -// } -// private void setNewItemModel(ItemMeta meta, ConfigurationSection section) { -// setNewItemModel("default", meta, section); -// } -// private void setNewItemModel(String key, ItemMeta meta, ConfigurationSection section) { -// String modelLocation = section.getString(key, "minecraft:bucket"); -// String[] split = modelLocation.split(":"); -// NamespacedKey namespacedKey = new NamespacedKey(split[0], split[1]); -// meta.setItemModel(namespacedKey); -// } + public String getItemModel(LivingEntity entity) { + return texture.getString(entity.getType().toString().toUpperCase() + ".default", "minecraft:bucket"); + } } diff --git a/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java b/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java index d05a74a..e7dee58 100644 --- a/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java +++ b/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java @@ -2,19 +2,27 @@ import io.papermc.paper.datacomponent.DataComponentTypes; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import org.bukkit.Bukkit; import org.bukkit.Material; +import org.bukkit.NamespacedKey; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataType; +import simplexity.simplebucketmobs.SimpleBucketMobs; +import simplexity.simplebucketmobs.config.Config; +import simplexity.simplebucketmobs.config.Texture; import java.util.HashMap; @SuppressWarnings({"UnstableApiUsage", "deprecation"}) public class BucketHandler { + private static final MiniMessage miniMessage = SimpleBucketMobs.getMiniMessage(); + public static ItemStack getMobBucket(LivingEntity entity) { ItemStack bucketStack = new ItemStack(Material.BUCKET); bucketStack.setData(DataComponentTypes.MAX_STACK_SIZE, 1); @@ -22,6 +30,12 @@ public static ItemStack getMobBucket(LivingEntity entity) { bucketStack.setData(DataComponentTypes.CUSTOM_NAME, nameComponent); bucketStack.setData(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, true); byte[] serializedEntity = Bukkit.getUnsafe().serializeEntity(entity); + if (Config.getInstance().isUseResourcePack()) { + String itemModelString = Texture.getInstance().getItemModel(entity); + String[] split = itemModelString.split(":"); + NamespacedKey namespacedKey = new NamespacedKey(split[0], split[1]); + bucketStack.setData(DataComponentTypes.ITEM_MODEL, namespacedKey); + } bucketStack.editPersistentDataContainer(pdc -> pdc.set(BucketMob.newMobTag, PersistentDataType.BYTE_ARRAY, serializedEntity)); return bucketStack; } @@ -39,11 +53,40 @@ public static void addMobBucketToInventory(Player player, ItemStack bucket) { } private static Component getBucketName(LivingEntity entity) { - Component nameComponent = Component.empty(); - nameComponent = nameComponent.append(Component.text(entity.getType().toString().toLowerCase())); + Component entityName = entity.customName(); + if (entityName == null) entityName = entity.name(); + String typeName = entity.getType().name(); + String typeNameCased = nameCase(typeName); + String configName = Config.getInstance().getBucketTitle(); + return miniMessage.deserialize(configName, + Placeholder.component("display_name", entityName), + Placeholder.parsed("type", entity.getType().name()), + Placeholder.parsed("type_name_cased", typeNameCased)); + } - nameComponent = nameComponent.append(Component.text(" Bucket")); - return nameComponent; + /** + * Converts a string to Name Case + * + * @param input String + * @return String but using Name Case + * @implNote Thanks Baeldung (https://www.baeldung.com/java-string-title-case) + */ + private static String nameCase(String input) { + if (input == null || input.isBlank()) return input; + input = input.replace('_', ' '); + StringBuilder nameCased = new StringBuilder(); + boolean toUpper = true; + for (char c : input.toCharArray()) { + if (Character.isSpaceChar(c)) toUpper = true; + else if (toUpper) { + c = Character.toTitleCase(c); + toUpper = false; + } else { + c = Character.toLowerCase(c); + } + nameCased.append(c); + } + return nameCased.toString(); } } diff --git a/src/main/java/simplexity/simplebucketmobs/listener/BucketMob.java b/src/main/java/simplexity/simplebucketmobs/listener/BucketMob.java index 6be54c6..8e23220 100644 --- a/src/main/java/simplexity/simplebucketmobs/listener/BucketMob.java +++ b/src/main/java/simplexity/simplebucketmobs/listener/BucketMob.java @@ -1,12 +1,14 @@ package simplexity.simplebucketmobs.listener; import io.papermc.paper.persistence.PersistentDataContainerView; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.block.Block; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Monster; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -16,6 +18,7 @@ import simplexity.simplebucketmobs.SimpleBucketMobs; import simplexity.simplebucketmobs.config.Config; import simplexity.simplebucketmobs.util.BucketMobPermission; +import simplexity.simplebucketmobs.util.Message; public class BucketMob implements Listener { @@ -28,6 +31,14 @@ public void onBucketMob(PlayerInteractEntityEvent interactEvent) { Player player = interactEvent.getPlayer(); if (!(entity instanceof LivingEntity livingEntity)) return; EntityType type = livingEntity.getType(); + if (livingEntity instanceof Monster monster) { + LivingEntity target = monster.getTarget(); + if (target != null && Config.getInstance().isNoHostileTargeting() && monster.isAggressive() && target.equals(player)) { + player.sendRichMessage(Message.ERROR_BUCKET_HOSTILE_TARGETING.getMessage(), + Placeholder.parsed("prefix", Message.PREFIX.getMessage())); + return; + } + } if (!Config.getInstance().getAllowedBasicTypes().contains(type)) return; if (!(player.hasPermission(BucketMobPermission.BUCKET_MOB_BASE + type.toString().toLowerCase()) || player.hasPermission(BucketMobPermission.BUCKET_ALL))) return; @@ -57,6 +68,4 @@ public void onUnbucketMob(PlayerInteractEvent interactEvent) { if (bucketPdc.has(newMobTag)) EntityHandler.handleMobSpawn(player, itemStack, block); if (bucketPdc.has(legacyMobTag)) EntityHandler.handleLegacyMobSpawn(player, itemStack, block); } - - } diff --git a/src/main/java/simplexity/simplebucketmobs/util/BucketMobPermission.java b/src/main/java/simplexity/simplebucketmobs/util/BucketMobPermission.java index 0176608..109a85d 100644 --- a/src/main/java/simplexity/simplebucketmobs/util/BucketMobPermission.java +++ b/src/main/java/simplexity/simplebucketmobs/util/BucketMobPermission.java @@ -9,5 +9,5 @@ public class BucketMobPermission { public static final Permission COMMAND_RELOAD = new Permission("simplebucketmobs.reload", "Reload plugin configuration files.", PermissionDefault.OP); public static final Permission BUCKET_MOB_BASE = new Permission("simplebucketmobs.bucket.", "Allows the user to bucket the specified mob.", PermissionDefault.OP); - public static final Permission BUCKET_ALL = new Permission("simplebucketmobs.bucket.all", "Allows the user to bucket all enabled mobs.", PermissionDefault.TRUE); + public static final Permission BUCKET_ALL = new Permission("simplebucketmobs.bucket.all", "Allows the user to bucket all enabled mobs.", PermissionDefault.OP); } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index a01c592..c7ec51d 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -21,3 +21,4 @@ permissions: default: op simplebucketmobs.debucket: description: "Dump saved mob NBT data from Mob Bucket to chat." + default: op From ad9badc9c8f847847d1564403c1f485b537d17ca Mon Sep 17 00:00:00 2001 From: Rhythmic System Date: Sat, 26 Jul 2025 15:33:28 -0700 Subject: [PATCH 06/11] Move to maven - add support for variants --- build.gradle.kts | 39 -- gradle/wrapper/gradle-wrapper.properties | 7 - gradlew | 249 --------- gradlew.bat | 92 ---- pom.xml | 81 +++ settings.gradle.kts | 7 - .../simplebucketmobs/config/Texture.java | 139 ++++- .../listener/BucketHandler.java | 18 +- src/main/resources/texture.yml | 496 +++++++++--------- 9 files changed, 485 insertions(+), 643 deletions(-) delete mode 100644 build.gradle.kts delete mode 100644 gradle/wrapper/gradle-wrapper.properties delete mode 100644 gradlew delete mode 100644 gradlew.bat create mode 100644 pom.xml delete mode 100644 settings.gradle.kts diff --git a/build.gradle.kts b/build.gradle.kts deleted file mode 100644 index 2e044b7..0000000 --- a/build.gradle.kts +++ /dev/null @@ -1,39 +0,0 @@ -version = "1.3.0" - -plugins { - `java-library` - id("io.papermc.paperweight.userdev") version "2.0.0-beta.17" - -} - -java { - toolchain.languageVersion.set(JavaLanguageVersion.of(21)) -} - -repositories { - maven("https://repo.papermc.io/repository/maven-public/") -} - -dependencies { - paperweight.paperDevBundle("1.21.7-R0.1-SNAPSHOT") -} - -tasks { - assemble { - paperweight.reobfArtifactConfiguration = io.papermc.paperweight.userdev.ReobfArtifactConfiguration.MOJANG_PRODUCTION - } - - compileJava { - options.encoding = "UTF-8" - options.release.set(21) - } - javadoc { - options.encoding = "UTF-8" - } - processResources { - filteringCharset = "UTF-8" - filesMatching("plugin.yml") { - expand("version" to project.version) - } - } -} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index ca025c8..0000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew deleted file mode 100644 index b740cf1..0000000 --- a/gradlew +++ /dev/null @@ -1,249 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index 25da30d..0000000 --- a/gradlew.bat +++ /dev/null @@ -1,92 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..cfc1624 --- /dev/null +++ b/pom.xml @@ -0,0 +1,81 @@ + + + 4.0.0 + + com.simplexity + SimpleBucketMobs + 1.3.0 + jar + SimpleBucketMobs + + + 21 + UTF-8 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 21 + 21 + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + false + + + + mojang + + + + + + + + + + + src/main/resources + true + + + + + + + papermc-repo + https://repo.papermc.io/repository/maven-public/ + + + sonatype + https://oss.sonatype.org/content/groups/public/ + + + + + + io.papermc.paper + paper-api + 1.21.6-R0.1-SNAPSHOT + provided + + + diff --git a/settings.gradle.kts b/settings.gradle.kts deleted file mode 100644 index b5bd391..0000000 --- a/settings.gradle.kts +++ /dev/null @@ -1,7 +0,0 @@ -pluginManagement { - repositories { - gradlePluginPortal() - maven("https://repo.papermc.io/repository/maven-public/") - } -} -rootProject.name = "SimpleBucketMobs" diff --git a/src/main/java/simplexity/simplebucketmobs/config/Texture.java b/src/main/java/simplexity/simplebucketmobs/config/Texture.java index b21f467..257a396 100644 --- a/src/main/java/simplexity/simplebucketmobs/config/Texture.java +++ b/src/main/java/simplexity/simplebucketmobs/config/Texture.java @@ -1,14 +1,32 @@ package simplexity.simplebucketmobs.config; +import org.bukkit.DyeColor; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Cat; +import org.bukkit.entity.Chicken; +import org.bukkit.entity.Cow; +import org.bukkit.entity.Fox; +import org.bukkit.entity.Frog; +import org.bukkit.entity.Horse; import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Llama; +import org.bukkit.entity.MushroomCow; +import org.bukkit.entity.Parrot; +import org.bukkit.entity.Pig; +import org.bukkit.entity.Rabbit; +import org.bukkit.entity.Sheep; +import org.bukkit.entity.Shulker; +import org.bukkit.entity.TraderLlama; +import org.bukkit.entity.Villager; +import org.bukkit.entity.Wolf; import simplexity.simplebucketmobs.SimpleBucketMobs; import java.io.File; import java.io.IOException; +@SuppressWarnings({"CallToPrintStackTrace"}) public class Texture { private static Texture instance; @@ -40,7 +58,126 @@ public void reloadTextureConfig() { } public String getItemModel(LivingEntity entity) { - return texture.getString(entity.getType().toString().toUpperCase() + ".default", "minecraft:bucket"); + if (entity instanceof Cat cat) return getCatItemModel(cat); + if (entity instanceof Chicken chicken) return getChickenItemModel(chicken); + if (entity instanceof Cow cow) return getCowItemModel(cow); + if (entity instanceof Fox fox) return getFoxItemModel(fox); + if (entity instanceof Frog frog) return getFrogItemModel(frog); + if (entity instanceof Horse horse) return getHorseItemModel(horse); + // Have to do this first otherwise llama will eat it + if (entity instanceof TraderLlama traderLlama) return getTraderLlamaItemModel(traderLlama); + if (entity instanceof Llama llama) return getLlamaItemModel(llama); + if (entity instanceof MushroomCow mooshroom) return getMooshroomItemModel(mooshroom); + if (entity instanceof Parrot parrot) return getParrotItemModel(parrot); + if (entity instanceof Pig pig) return getPigItemModel(pig); + if (entity instanceof Rabbit rabbit) return getRabbitItemModel(rabbit); + if (entity instanceof Sheep sheep) return getSheepItemModel(sheep); + if (entity instanceof Shulker shulker) return getShulkerItemModel(shulker); + if (entity instanceof Villager villager) return getVillagerItemModel(villager); + if (entity instanceof Wolf wolf) return getWolfItemModel(wolf); + return texture.getString(entity.getType().toString().toLowerCase() + ".default", "minecraft:bucket"); + } + + public String getCatItemModel(Cat cat) { + Cat.Type catType = cat.getCatType(); + String defaultTexture = texture.getString("cat.default", "minecraft:bucket"); + return texture.getString("cat.type." + catType.toString().toLowerCase(), defaultTexture); + } + + public String getChickenItemModel(Chicken chicken) { + Chicken.Variant chickenType = chicken.getVariant(); + String defaultTexture = texture.getString("chicken.default", "minecraft:bucket"); + return texture.getString("chicken.type." + chickenType.toString().toLowerCase(), defaultTexture); + } + + public String getCowItemModel(Cow cow) { + Cow.Variant cowType = cow.getVariant(); + String defaultTexture = texture.getString("cow.default", "minecraft:bucket"); + return texture.getString("cow.type." + cowType.toString().toLowerCase(), defaultTexture); + } + + public String getFoxItemModel(Fox fox) { + Fox.Type foxType = fox.getFoxType(); + String defaultTexture = texture.getString("fox.default", "minecraft:bucket"); + return texture.getString("fox.type." + foxType.toString().toLowerCase(), defaultTexture); + } + + public String getFrogItemModel(Frog frog) { + Frog.Variant frogType = frog.getVariant(); + String defaultTexture = texture.getString("frog.default", "minecraft:bucket"); + return texture.getString("frog.type." + frogType.toString().toLowerCase(), defaultTexture); + } + + public String getHorseItemModel(Horse horse) { + Horse.Color color = horse.getColor(); + Horse.Style style = horse.getStyle(); + String defaultTexture = texture.getString("horse.default", "minecraft:bucket"); + String colorKey = color.name().toLowerCase(); + String styleKey = style.name().toLowerCase(); + String key = colorKey + "_" + styleKey; + return texture.getString("horse.type." + key, defaultTexture); + } + + public String getLlamaItemModel(Llama llama) { + Llama.Color llamaColor = llama.getColor(); + String defaultTexture = texture.getString("llama.default", "minecraft:bucket"); + return texture.getString("llama.type." + llamaColor.toString().toLowerCase(), defaultTexture); + } + + public String getMooshroomItemModel(MushroomCow mooshroom) { + MushroomCow.Variant mooshroomType = mooshroom.getVariant(); + String defaultTexture = texture.getString("mooshroom.default", "minecraft:bucket"); + return texture.getString("mooshroom.type." + mooshroomType.toString().toLowerCase(), defaultTexture); + } + + public String getParrotItemModel(Parrot parrot) { + Parrot.Variant parrotType = parrot.getVariant(); + String defaultTexture = texture.getString("parrot.default", "minecraft:bucket"); + return texture.getString("parrot.type." + parrotType.toString().toLowerCase(), defaultTexture); + } + + public String getPigItemModel(Pig pig) { + Pig.Variant pigType = pig.getVariant(); + String defaultTexture = texture.getString("pig.default", "minecraft:bucket"); + return texture.getString("pig.type." + pigType.toString().toLowerCase(), defaultTexture); + } + + public String getRabbitItemModel(Rabbit rabbit) { + Rabbit.Type rabbitType = rabbit.getRabbitType(); + String defaultTexture = texture.getString("rabbit.default", "minecraft:bucket"); + return texture.getString("rabbit.type." + rabbitType.toString().toLowerCase(), defaultTexture); + } + + public String getSheepItemModel(Sheep sheep) { + DyeColor sheepType = sheep.getColor(); + String defaultTexture = texture.getString("sheep.default", "minecraft:bucket"); + if (sheepType == null) return defaultTexture; + return texture.getString("sheep.type." + sheepType.toString().toLowerCase(), defaultTexture); + } + + public String getShulkerItemModel(Shulker shulker) { + DyeColor shulkerType = shulker.getColor(); + String defaultTexture = texture.getString("shulker.default", "minecraft:bucket"); + if (shulkerType == null) return defaultTexture; + return texture.getString("shulker.type." + shulkerType.toString().toLowerCase(), defaultTexture); + } + + public String getTraderLlamaItemModel(TraderLlama traderLlama) { + Llama.Color llamaColor = traderLlama.getColor(); + String defaultTexture = texture.getString("trader_llama.default", "minecraft:bucket"); + return texture.getString("trader_llama.type." + llamaColor.toString().toLowerCase(), defaultTexture); + } + + public String getVillagerItemModel(Villager villager) { + Villager.Profession villagerProfession = villager.getProfession(); + String defaultTexture = texture.getString("villager.default", "minecraft:bucket"); + return texture.getString("villager.type." + villagerProfession.toString().toLowerCase(), defaultTexture); + } + + public String getWolfItemModel(Wolf wolf) { + Wolf.Variant wolfType = wolf.getVariant(); + String defaultTexture = texture.getString("wolf.default", "minecraft:bucket"); + return texture.getString("wolf.type." + wolfType.toString().toLowerCase(), defaultTexture); } } diff --git a/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java b/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java index e7dee58..c0084a7 100644 --- a/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java +++ b/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java @@ -32,9 +32,21 @@ public static ItemStack getMobBucket(LivingEntity entity) { byte[] serializedEntity = Bukkit.getUnsafe().serializeEntity(entity); if (Config.getInstance().isUseResourcePack()) { String itemModelString = Texture.getInstance().getItemModel(entity); - String[] split = itemModelString.split(":"); - NamespacedKey namespacedKey = new NamespacedKey(split[0], split[1]); - bucketStack.setData(DataComponentTypes.ITEM_MODEL, namespacedKey); + + if (itemModelString == null || !itemModelString.contains(":")) { + Bukkit.getLogger().warning("Invalid or missing item model string for entity: " + entity.getType() + + ". Value: '" + itemModelString + "'"); + } else { + String[] split = itemModelString.trim().split(":"); + + if (split.length != 2 || split[0].isEmpty() || split[1].isEmpty()) { + Bukkit.getLogger().warning("Malformed item model string for entity: " + entity.getType() + + ". Value: '" + itemModelString + "'"); + } else { + NamespacedKey namespacedKey = new NamespacedKey(split[0], split[1]); + bucketStack.setData(DataComponentTypes.ITEM_MODEL, namespacedKey); + } + } } bucketStack.editPersistentDataContainer(pdc -> pdc.set(BucketMob.newMobTag, PersistentDataType.BYTE_ARRAY, serializedEntity)); return bucketStack; diff --git a/src/main/resources/texture.yml b/src/main/resources/texture.yml index 69a97f0..64b532a 100644 --- a/src/main/resources/texture.yml +++ b/src/main/resources/texture.yml @@ -10,326 +10,332 @@ # ... # {nbtValue}: {custom model data} ### -ALLAY: +allay: default: "simplexity:bucket_mobs/misc/allay" -ARMADILLO: +armadillo: default: "simplexity:bucket_mobs/misc/armadillo" -BAT: +bat: default: "simplexity:bucket_mobs/misc/bat" -BEE: +bee: default: "simplexity:bucket_mobs/misc/bee" -BLAZE: +blaze: default: "simplexity:bucket_mobs/misc/blaze" -BOGGED: +bogged: default: "simplexity:bucket_mobs/skeleton/bogged" -BREEZE: +breeze: default: "simplexity:bucket_mobs/misc/breeze" -CAMEL: +camel: default: "simplexity:bucket_mobs/misc/camel" -CAT: +cat: default: "simplexity:bucket_mobs/cat/black" - variant: - "minecraft:tabby": "simplexity:bucket_mobs/cat/tabby" - "minecraft:black": "simplexity:bucket_mobs/cat/black" - "minecraft:red": "simplexity:bucket_mobs/cat/red" - "minecraft:siamese": "simplexity:bucket_mobs/cat/siamese" - "minecraft:british_shorthair": "simplexity:bucket_mobs/cat/british_shorthair" - "minecraft:calico": "simplexity:bucket_mobs/cat/calico" - "minecraft:persian": "simplexity:bucket_mobs/cat/persian" - "minecraft:ragdoll": "simplexity:bucket_mobs/cat/ragdoll" - "minecraft:white": "simplexity:bucket_mobs/cat/white" - "minecraft:jellie": "simplexity:bucket_mobs/cat/jellie" - "minecraft:all_black": "simplexity:bucket_mobs/cat/all_black" -CAVE_SPIDER: + type: + tabby: "simplexity:bucket_mobs/cat/tabby" + black: "simplexity:bucket_mobs/cat/black" + red: "simplexity:bucket_mobs/cat/red" + siamese: "simplexity:bucket_mobs/cat/siamese" + british_shorthair: "simplexity:bucket_mobs/cat/british_shorthair" + calico: "simplexity:bucket_mobs/cat/calico" + persian: "simplexity:bucket_mobs/cat/persian" + ragdoll: "simplexity:bucket_mobs/cat/ragdoll" + white: "simplexity:bucket_mobs/cat/white" + jellie: "simplexity:bucket_mobs/cat/jellie" + all_black: "simplexity:bucket_mobs/cat/all_black" +cave_spider: default: "simplexity:bucket_mobs/misc/cave_spider" -CHICKEN: - default: "simplexity:bucket_mobs/misc/chicken" -COW: - default: "simplexity:bucket_mobs/cow/cow" -CREAKING: +chicken: + default: "simplexity:bucket_mobs/chicken/temperate" + type: + cold: "simplexity:bucket_mobs/chicken/cold" + temperate: "simplexity:bucket_mobs/chicken/temperate" + warm: "simplexity:bucket_mobs/chicken/warm" +cow: + default: "simplexity:bucket_mobs/cow/temperate" + type: + cold: "simplexity:bucket_mobs/cow/cold" + temperate: "simplexity:bucket_mobs/cow/temperate" + warm: "simplexity:bucket_mobs/cow/warm" +creaking: default: "simplexity:bucket_mobs/misc/creaking" -CREEPER: +creeper: default: "simplexity:bucket_mobs/misc/creeper" - powered: - 1b: "simplexity:bucket_mobs/misc/charged_creeper" -DOLPHIN: + type: + powered: "simplexity:bucket_mobs/misc/charged_creeper" +dolphin: default: "simplexity:bucket_mobs/misc/dolphin" -DONKEY: +donkey: default: "simplexity:bucket_mobs/horse/donkey_default" -DROWNED: +drowned: default: "simplexity:bucket_mobs/zombie/drowned" -ELDER_GUARDIAN: +elder_guardian: default: "simplexity:bucket_mobs/misc/elder_guardian" -ENDER_DRAGON: +ender_dragon: default: "simplexity:bucket_mobs/misc/ender_dragon" -ENDERMAN: +enderman: default: "simplexity:bucket_mobs/misc/enderman" -ENDERMITE: +endermite: default: "simplexity:bucket_mobs/misc/endermite" -EVOKER: +evoker: default: "simplexity:bucket_mobs/illager/evoker" -FOX: +fox: default: "simplexity:bucket_mobs/misc/fox" - Type: + type: red: "simplexity:bucket_mobs/misc/fox" snow: "simplexity:bucket_mobs/misc/snow_fox" -FROG: +frog: default: "simplexity:bucket_mobs/frog/cold" - variant: - "minecraft:cold": "simplexity:bucket_mobs/frog/cold" - "minecraft:temperate": "simplexity:bucket_mobs/frog/temperate" - "minecraft:warm": "simplexity:bucket_mobs/frog/warm" -GHAST: + type: + cold: "simplexity:bucket_mobs/frog/cold" + temperate: "simplexity:bucket_mobs/frog/temperate" + warm: "simplexity:bucket_mobs/frog/warm" +ghast: default: "simplexity:bucket_mobs/misc/ghast" -GLOW_SQUID: +glow_squid: default: "simplexity:bucket_mobs/misc/glow_squid" -GOAT: +goat: default: "simplexity:bucket_mobs/misc/goat" -GIANT: +giant: default: "simplexity:bucket_mobs/zombie/zombie" -GUARDIAN: +guardian: default: "simplexity:bucket_mobs/misc/guardian" -HOGLIN: +hoglin: default: "simplexity:bucket_mobs/misc/hoglin" -HORSE: +horse: default: "simplexity:bucket_mobs/horse/brown_default" - Variant: - 0: "simplexity:bucket_mobs/horse/white_default" - 1: "simplexity:bucket_mobs/horse/creamy_default" - 2: "simplexity:bucket_mobs/horse/chestnut_default" - 3: "simplexity:bucket_mobs/horse/brown_default" - 4: "simplexity:bucket_mobs/horse/black_default" - 5: "simplexity:bucket_mobs/horse/gray_default" - 6: "simplexity:bucket_mobs/horse/dark_brown_default" - 256: "simplexity:bucket_mobs/horse/white_white_stockings_and_blaze" - 257: "simplexity:bucket_mobs/horse/creamy_white_stockings_and_blaze" - 258: "simplexity:bucket_mobs/horse/chestnut_white_stockings_and_blaze" - 259: "simplexity:bucket_mobs/horse/brown_white_stockings_and_blaze" - 260: "simplexity:bucket_mobs/horse/black_white_stockings_and_blaze" - 261: "simplexity:bucket_mobs/horse/gray_white_stockings_and_blaze" - 262: "simplexity:bucket_mobs/horse/dark_brown_white_stockings_and_blaze" - 512: "simplexity:bucket_mobs/horse/white_white_field" - 513: "simplexity:bucket_mobs/horse/creamy_white_field" - 514: "simplexity:bucket_mobs/horse/chestnut_white_field" - 515: "simplexity:bucket_mobs/horse/brown_white_field" - 516: "simplexity:bucket_mobs/horse/black_white_field" - 517: "simplexity:bucket_mobs/horse/gray_white_field" - 518: "simplexity:bucket_mobs/horse/dark_brown_white_field" - 768: "simplexity:bucket_mobs/horse/white_white_spots" - 769: "simplexity:bucket_mobs/horse/creamy_white_spots" - 770: "simplexity:bucket_mobs/horse/chestnut_white_spots" - 771: "simplexity:bucket_mobs/horse/brown_white_spots" - 772: "simplexity:bucket_mobs/horse/black_white_spots" - 773: "simplexity:bucket_mobs/horse/gray_white_spots" - 774: "simplexity:bucket_mobs/horse/dark_brown_spots" - 1024: "simplexity:bucket_mobs/horse/white_default" - 1025: "simplexity:bucket_mobs/horse/creamy_default" - 1026: "simplexity:bucket_mobs/horse/chestnut_default" - 1027: "simplexity:bucket_mobs/horse/brown_default" - 1028: "simplexity:bucket_mobs/horse/black_default" - 1029: "simplexity:bucket_mobs/horse/gray_default" - 1030: "simplexity:bucket_mobs/horse/dark_brown_default" -HUSK: + type: + white_none: "simplexity:bucket_mobs/horse/white_default" + creamy_none: "simplexity:bucket_mobs/horse/creamy_default" + chestnut_none: "simplexity:bucket_mobs/horse/chestnut_default" + brown_none: "simplexity:bucket_mobs/horse/brown_default" + black_none: "simplexity:bucket_mobs/horse/black_default" + gray_none: "simplexity:bucket_mobs/horse/gray_default" + dark_brown_none: "simplexity:bucket_mobs/horse/dark_brown_default" + white_white_stockings: "simplexity:bucket_mobs/horse/white_white_stockings_and_blaze" + creamy_white_stockings: "simplexity:bucket_mobs/horse/creamy_white_stockings_and_blaze" + chestnut_white_stockings: "simplexity:bucket_mobs/horse/chestnut_white_stockings_and_blaze" + brown_white_stockings: "simplexity:bucket_mobs/horse/brown_white_stockings_and_blaze" + black_white_stockings: "simplexity:bucket_mobs/horse/black_white_stockings_and_blaze" + gray_white_stockings: "simplexity:bucket_mobs/horse/gray_white_stockings_and_blaze" + dark_brown_white_stockings: "simplexity:bucket_mobs/horse/dark_brown_white_stockings_and_blaze" + white_white_field: "simplexity:bucket_mobs/horse/white_white_field" + creamy_white_field: "simplexity:bucket_mobs/horse/creamy_white_field" + chestnut_white_field: "simplexity:bucket_mobs/horse/chestnut_white_field" + brown_white_field: "simplexity:bucket_mobs/horse/brown_white_field" + black_white_field: "simplexity:bucket_mobs/horse/black_white_field" + gray_white_field: "simplexity:bucket_mobs/horse/gray_white_field" + dark_brown_white_field: "simplexity:bucket_mobs/horse/dark_brown_white_field" + white_spots: "simplexity:bucket_mobs/horse/white_white_spots" + creamy_spots: "simplexity:bucket_mobs/horse/creamy_white_spots" + chestnut_spots: "simplexity:bucket_mobs/horse/chestnut_white_spots" + brown_spots: "simplexity:bucket_mobs/horse/brown_white_spots" + black_spots: "simplexity:bucket_mobs/horse/black_white_spots" + gray_spots: "simplexity:bucket_mobs/horse/gray_white_spots" + dark_brown_spots: "simplexity:bucket_mobs/horse/dark_brown_spots" +husk: default: "simplexity:bucket_mobs/zombie/husk" -ILLUSIONER: +illusioner: default: "simplexity:bucket_mobs/illager/illusioner" -IRON_GOLEM: +iron_golem: default: "simplexity:bucket_mobs/misc/iron_golem" -LLAMA: +llama: default: "simplexity:bucket_mobs/llama/brown_default" - Variant: - 0: "simplexity:bucket_mobs/llama/creamy_default" # Creamy - 1: "simplexity:bucket_mobs/llama/white_default" # White - 2: "simplexity:bucket_mobs/llama/brown_default" # Brown - 3: "simplexity:bucket_mobs/llama/gray_default" # Gray -MAGMA_CUBE: + type: + creamy: "simplexity:bucket_mobs/llama/creamy_default" + white: "simplexity:bucket_mobs/llama/white_default" + brown: "simplexity:bucket_mobs/llama/brown_default" + gray: "simplexity:bucket_mobs/llama/gray_default" +magma_cube: default: "simplexity:bucket_mobs/misc/magma_cube" -MULE: +mule: default: "simplexity:bucket_mobs/horse/mule_default" -MUSHROOM_COW: +mushroom_cow: default: "simplexity:bucket_mobs/cow/mooshroom" - Type: + type: red: "simplexity:bucket_mobs/cow/mooshroom" brown: "simplexity:bucket_mobs/cow/brown_mooshroom" -OCELOT: +ocelot: default: "simplexity:bucket_mobs/cat/ocelot" -PANDA: +panda: default: "simplexity:bucket_mobs/panda/panda" -PARROT: +parrot: default: "simplexity:bucket_mobs/parrot/red_blue" - Variant: - 0: "simplexity:bucket_mobs/parrot/red_blue" # Red Blue - 1: "simplexity:bucket_mobs/parrot/blue" # Blue - 2: "simplexity:bucket_mobs/parrot/green" # Green - 3: "simplexity:bucket_mobs/parrot/yellow_blue" # Yellow Blue - 4: "simplexity:bucket_mobs/parrot/gray" # Gray -PHANTOM: + type: + red: "simplexity:bucket_mobs/parrot/red_blue" + blue: "simplexity:bucket_mobs/parrot/blue" + green: "simplexity:bucket_mobs/parrot/green" + cyan: "simplexity:bucket_mobs/parrot/yellow_blue" + gray: "simplexity:bucket_mobs/parrot/gray" +phantom: default: "simplexity:bucket_mobs/misc/phantom" -PIG: - default: "simplexity:bucket_mobs/misc/pig" -PIGLIN_BRUTE: +pig: + default: "simplexity:bucket_mobs/pig/temperate" + type: + cold: "simplexity:bucket_mobs/pig/cold" + temperate: "simplexity:bucket_mobs/pig/temperate" + warm: "simplexity:bucket_mobs/pig/warm" +piglin_brute: default: "simplexity:bucket_mobs/piglin/piglin_brute" -PIGLIN: +piglin: default: "simplexity:bucket_mobs/piglin/piglin" -PILLAGER: +pillager: default: "simplexity:bucket_mobs/illager/pillager" -POLAR_BEAR: +polar_bear: default: "simplexity:bucket_mobs/misc/polar_bear" -RABBIT: +rabbit: default: "simplexity:bucket_mobs/rabbit/brown" - RabbitType: - 0: "simplexity:bucket_mobs/rabbit/brown" # Brown - 1: "simplexity:bucket_mobs/rabbit/white" # White - 2: "simplexity:bucket_mobs/rabbit/black" # Black - 3: "simplexity:bucket_mobs/rabbit/white_splotched" # White Splotched - 4: "simplexity:bucket_mobs/rabbit/gold" # Gold - 5: "simplexity:bucket_mobs/rabbit/toast" # Salt and Pepper - 99: "simplexity:bucket_mobs/rabbit/caerbannog" # Evil / Killer -RAVAGER: + type: + brown: "simplexity:bucket_mobs/rabbit/brown" + white: "simplexity:bucket_mobs/rabbit/white" + black: "simplexity:bucket_mobs/rabbit/black" + black_and_white: "simplexity:bucket_mobs/rabbit/white_splotched" + gold: "simplexity:bucket_mobs/rabbit/gold" + salt_and_pepper: "simplexity:bucket_mobs/rabbit/toast" + the_killer_bunny: "simplexity:bucket_mobs/rabbit/caerbannog" +ravager: default: "simplexity:bucket_mobs/illager/ravager" -SHEEP: +sheep: default: "simplexity:bucket_mobs/sheep/white" - Color: - 0b: "simplexity:bucket_mobs/sheep/white" # White - 1b: "simplexity:bucket_mobs/sheep/orange" # Orange - 2b: "simplexity:bucket_mobs/sheep/magenta" # Magenta - 3b: "simplexity:bucket_mobs/sheep/light_blue" # Light Blue - 4b: "simplexity:bucket_mobs/sheep/yellow" # Yellow - 5b: "simplexity:bucket_mobs/sheep/lime" # Lime - 6b: "simplexity:bucket_mobs/sheep/pink" # Pink - 7b: "simplexity:bucket_mobs/sheep/gray" # Gray - 8b: "simplexity:bucket_mobs/sheep/light_gray" # Light Gray - 9b: "simplexity:bucket_mobs/sheep/cyan" # Cyan - 10b: "simplexity:bucket_mobs/sheep/purple" # Purple - 11b: "simplexity:bucket_mobs/sheep/blue" # Blue - 12b: "simplexity:bucket_mobs/sheep/brown" # Brown - 13b: "simplexity:bucket_mobs/sheep/green" # Green - 14b: "simplexity:bucket_mobs/sheep/red" # Red - 15b: "simplexity:bucket_mobs/sheep/black" # Black -SHULKER: + type: + white: "simplexity:bucket_mobs/sheep/white" + orange: "simplexity:bucket_mobs/sheep/orange" + magenta: "simplexity:bucket_mobs/sheep/magenta" + light_blue: "simplexity:bucket_mobs/sheep/light_blue" + yellow: "simplexity:bucket_mobs/sheep/yellow" + lime: "simplexity:bucket_mobs/sheep/lime" + pink: "simplexity:bucket_mobs/sheep/pink" + gray: "simplexity:bucket_mobs/sheep/gray" + light_gray: "simplexity:bucket_mobs/sheep/light_gray" + cyan: "simplexity:bucket_mobs/sheep/cyan" + purple: "simplexity:bucket_mobs/sheep/purple" + blue: "simplexity:bucket_mobs/sheep/blue" + brown: "simplexity:bucket_mobs/sheep/brown" + green: "simplexity:bucket_mobs/sheep/green" + red: "simplexity:bucket_mobs/sheep/red" + black: "simplexity:bucket_mobs/sheep/black" +shulker: default: "simplexity:bucket_mobs/shulker/default" - Color: - 0b: "simplexity:bucket_mobs/shulker/white" # White - 1b: "simplexity:bucket_mobs/shulker/orange" # Orange - 2b: "simplexity:bucket_mobs/shulker/magenta" # Magenta - 3b: "simplexity:bucket_mobs/shulker/light_blue" # Light Blue - 4b: "simplexity:bucket_mobs/shulker/yellow" # Yellow - 5b: "simplexity:bucket_mobs/shulker/lime" # Lime - 6b: "simplexity:bucket_mobs/shulker/pink" # Pink - 7b: "simplexity:bucket_mobs/shulker/gray" # Gray - 8b: "simplexity:bucket_mobs/shulker/light_gray" # Light Gray - 9b: "simplexity:bucket_mobs/shulker/cyan" # Cyan - 10b: "simplexity:bucket_mobs/shulker/purple" # Purple - 11b: "simplexity:bucket_mobs/shulker/blue" # Blue - 12b: "simplexity:bucket_mobs/shulker/brown" # Brown - 13b: "simplexity:bucket_mobs/shulker/green" # Green - 14b: "simplexity:bucket_mobs/shulker/red" # Red - 15b: "simplexity:bucket_mobs/shulker/black" # Black - 16b: "simplexity:bucket_mobs/shulker/default" -SILVERFISH: + type: + white: "simplexity:bucket_mobs/shulker/white" + orange: "simplexity:bucket_mobs/shulker/orange" + magenta: "simplexity:bucket_mobs/shulker/magenta" + light_blue: "simplexity:bucket_mobs/shulker/light_blue" + yellow: "simplexity:bucket_mobs/shulker/yellow" + lime: "simplexity:bucket_mobs/shulker/lime" + pink: "simplexity:bucket_mobs/shulker/pink" + gray: "simplexity:bucket_mobs/shulker/gray" + light_gray: "simplexity:bucket_mobs/shulker/light_gray" + cyan: "simplexity:bucket_mobs/shulker/cyan" + purple: "simplexity:bucket_mobs/shulker/purple" + blue: "simplexity:bucket_mobs/shulker/blue" + brown: "simplexity:bucket_mobs/shulker/brown" + green: "simplexity:bucket_mobs/shulker/green" + red: "simplexity:bucket_mobs/shulker/red" + black: "simplexity:bucket_mobs/shulker/black" + default: "simplexity:bucket_mobs/shulker/default" +silverfish: default: "simplexity:bucket_mobs/misc/silver_fish" -SKELETON: +skeleton: default: "simplexity:bucket_mobs/skeleton/skeleton" -SKELETON_HORSE: +skeleton_horse: default: "simplexity:bucket_mobs/horse/skeleton_default" -SLIME: +slime: default: "simplexity:bucket_mobs/misc/slime" -SNIFFER: +sniffer: default: "simplexity:bucket_mobs/misc/sniffer" -SNOWMAN: +snowman: default: "simplexity:bucket_mobs/misc/pumpkin_golem" -SPIDER: +spider: default: "simplexity:bucket_mobs/misc/spider" -SQUID: +squid: default: "simplexity:bucket_mobs/misc/squid" -STRAY: +stray: default: "simplexity:bucket_mobs/skeleton/stray" -STRIDER: +strider: default: "simplexity:bucket_mobs/misc/warm_strider" -TRADER_LLAMA: +trader_llama: default: "simplexity:bucket_mobs/llama/brown_trader" - Variant: - 0: "simplexity:bucket_mobs/llama/creamy_trader" # Creamy - 1: "simplexity:bucket_mobs/llama/white_trader" # White - 2: "simplexity:bucket_mobs/llama/brown_trader" # Brown - 3: "simplexity:bucket_mobs/llama/gray_trader" # Gray -TURTLE: + type: + creamy: "simplexity:bucket_mobs/llama/creamy_trader" # Creamy + white: "simplexity:bucket_mobs/llama/white_trader" # White + brown: "simplexity:bucket_mobs/llama/brown_trader" # Brown + gray: "simplexity:bucket_mobs/llama/gray_trader" # Gray +turtle: default: "simplexity:bucket_mobs/misc/turtle" -VEX: +vex: default: "simplexity:bucket_mobs/misc/vex" -VILLAGER: +villager: default: "simplexity:bucket_mobs/villager/default_default" - VillagerData: - profession: - "minecraft:armorer": "simplexity:bucket_mobs/villager/armorer" - "minecraft:butcher": "simplexity:bucket_mobs/villager/butcher" - "minecraft:cartographer": "simplexity:bucket_mobs/villager/default_default" - "minecraft:cleric": "simplexity:bucket_mobs/villager/default_cleric" - "minecraft:farmer": "simplexity:bucket_mobs/villager/default_farmer" - "minecraft:fisherman": "simplexity:bucket_mobs/villager/default_fisherman" - "minecraft:fletcher": "simplexity:bucket_mobs/villager/default_fletcher" - "minecraft:leatherworker": "simplexity:bucket_mobs/villager/default_default" - "minecraft:librarian": "simplexity:bucket_mobs/villager/default_librarian" - "minecraft:nitwit": "simplexity:bucket_mobs/villager/default_default" - "minecraft:none": "simplexity:bucket_mobs/villager/default_default" - "minecraft:mason": "simplexity:bucket_mobs/villager/default_default" - "minecraft:shepherd": "simplexity:bucket_mobs/villager/default_shepherd" - "minecraft:toolsmith": "simplexity:bucket_mobs/villager/default_default" - "minecraft:weaponsmith": "simplexity:bucket_mobs/villager/default_weaponsmith" -VINDICATOR: + type: + armorer: "simplexity:bucket_mobs/villager/armorer" + butcher: "simplexity:bucket_mobs/villager/butcher" + cartographer: "simplexity:bucket_mobs/villager/default_default" + cleric: "simplexity:bucket_mobs/villager/default_cleric" + farmer: "simplexity:bucket_mobs/villager/default_farmer" + fisherman: "simplexity:bucket_mobs/villager/default_fisherman" + fletcher: "simplexity:bucket_mobs/villager/default_fletcher" + leatherworker: "simplexity:bucket_mobs/villager/default_default" + librarian: "simplexity:bucket_mobs/villager/default_librarian" + nitwit: "simplexity:bucket_mobs/villager/default_default" + none: "simplexity:bucket_mobs/villager/default_default" + mason: "simplexity:bucket_mobs/villager/default_default" + shepherd: "simplexity:bucket_mobs/villager/default_shepherd" + toolsmith: "simplexity:bucket_mobs/villager/default_default" + weaponsmith: "simplexity:bucket_mobs/villager/default_weaponsmith" +vindicator: default: "simplexity:bucket_mobs/illager/vindicator" -WANDERING_TRADER: +wandering_trader: default: "simplexity:bucket_mobs/misc/wandering_trader" -WARDEN: +warden: default: "simplexity:bucket_mobs/misc/warden" -WITCH: +witch: default: "simplexity:bucket_mobs/misc/witch" -WITHER: +wither: default: "simplexity:bucket_mobs/misc/wither" -WITHER_SKELETON: +wither_skeleton: default: "simplexity:bucket_mobs/skeleton/wither_skeleton" -WOLF: +wolf: default: "simplexity:bucket_mobs/wolf/pale_default" - variant: - "minecraft:ashen": "simplexity:bucket_mobs/wolf/ashen_default" - "minecraft:black": "simplexity:bucket_mobs/wolf/black_default" - "minecraft:chestnut" : "simplexity:bucket_mobs/wolf/chestnut_default" - "minecraft:pale" : "simplexity:bucket_mobs/wolf/pale_default" - "minecraft:rusty" : "simplexity:bucket_mobs/wolf/rusty_default" - "minecraft:snowy" : "simplexity:bucket_mobs/wolf/snowy_default" - "minecraft:spotted" : "simplexity:bucket_mobs/wolf/spotted_default" - "minecraft:striped" : "simplexity:bucket_mobs/wolf/striped_default" - "minecraft:woods" : "simplexity:bucket_mobs/wolf/woods_default" - # Tamed Wolf is temporarily in "special" (22049). -ZOMBIE: + type: + ashen: "simplexity:bucket_mobs/wolf/ashen_default" + ashen_tamed: "simplexity:bucket_mobs/wolf/ashen_tamed" + black: "simplexity:bucket_mobs/wolf/black_default" + black_tamed: "simplexity:bucket_mobs/wolf/black_tamed" + chestnut: "simplexity:bucket_mobs/wolf/chestnut_default" + chestnut_tamed: "simplexity:bucket_mobs/wolf/chestnut_tamed" + pale: "simplexity:bucket_mobs/wolf/pale_default" + pale_tamed: "simplexity:bucket_mobs/wolf/pale_tamed" + rusty: "simplexity:bucket_mobs/wolf/rusty_default" + rusty_tamed: "simplexity:bucket_mobs/wolf/rusty_tamed" + snowy: "simplexity:bucket_mobs/wolf/snowy_default" + snowy_tamed: "simplexity:bucket_mobs/wolf/snowy_tamed" + spotted: "simplexity:bucket_mobs/wolf/spotted_default" + spotted_tamed: "simplexity:bucket_mobs/wolf/spotted_tamed" + striped: "simplexity:bucket_mobs/wolf/striped_default" + striped_tamed: "simplexity:bucket_mobs/wolf/striped_tamed" + woods: "simplexity:bucket_mobs/wolf/woods_default" + woods_tamed: "simplexity:bucket_mobs/wolf/woods_tamed" +zombie: default: "simplexity:bucket_mobs/zombie/zombie" -ZOMBIE_VILLAGER: +zombie_villager: default: "simplexity:bucket_mobs/zombie_villager/default_default" - VillagerData: - profession: - "minecraft:armorer": "simplexity:bucket_mobs/zombie_villager/armorer" - "minecraft:butcher": "simplexity:bucket_mobs/zombie_villager/butcher" - "minecraft:cartographer": "simplexity:bucket_mobs/zombie_villager/default_default" - "minecraft:cleric": "simplexity:bucket_mobs/zombie_villager/default_cleric" - "minecraft:farmer": "simplexity:bucket_mobs/zombie_villager/default_farmer" - "minecraft:fisherman": "simplexity:bucket_mobs/zombie_villager/default_fisherman" - "minecraft:fletcher": "simplexity:bucket_mobs/zombie_villager/default_fletcher" - "minecraft:leatherworker": "simplexity:bucket_mobs/zombie_villager/default_default" - "minecraft:librarian": "simplexity:bucket_mobs/zombie_villager/default_librarian" - "minecraft:nitwit": "simplexity:bucket_mobs/zombie_villager/default_default" - "minecraft:none": "simplexity:bucket_mobs/zombie_villager/default_default" - "minecraft:mason": "simplexity:bucket_mobs/zombie_villager/default_default" - "minecraft:shepherd": "simplexity:bucket_mobs/zombie_villager/default_shepherd" - "minecraft:toolsmith": "simplexity:bucket_mobs/zombie_villager/default_default" - "minecraft:weaponsmith": "simplexity:bucket_mobs/zombie_villager/default_weaponsmith" -ZOMBIE_HORSE: + type: + armorer: "simplexity:bucket_mobs/zombie_villager/armorer" + butcher: "simplexity:bucket_mobs/zombie_villager/butcher" + cartographer: "simplexity:bucket_mobs/zombie_villager/default_default" + cleric: "simplexity:bucket_mobs/zombie_villager/default_cleric" + farmer: "simplexity:bucket_mobs/zombie_villager/default_farmer" + fisherman: "simplexity:bucket_mobs/zombie_villager/default_fisherman" + fletcher: "simplexity:bucket_mobs/zombie_villager/default_fletcher" + leatherworker: "simplexity:bucket_mobs/zombie_villager/default_default" + librarian: "simplexity:bucket_mobs/zombie_villager/default_librarian" + nitwit: "simplexity:bucket_mobs/zombie_villager/default_default" + none: "simplexity:bucket_mobs/zombie_villager/default_default" + mason: "simplexity:bucket_mobs/zombie_villager/default_default" + shepherd: "simplexity:bucket_mobs/zombie_villager/default_shepherd" + toolsmith: "simplexity:bucket_mobs/zombie_villager/default_default" + weaponsmith: "simplexity:bucket_mobs/zombie_villager/default_weaponsmith" +zombie_horse: default: "simplexity:bucket_mobs/horse/zombie_default" -ZOGLIN: +zoglin: default: "simplexity:bucket_mobs/misc/zombified_hoglin" -ZOMBIFIED_PIGLIN: +zombified_piglin: default: "simplexity:bucket_mobs/piglin/zombified_piglin" - -special: - fried: "simplexity:bucket_mobs/misc/chicken" - tamed_wolf: "simplexity:bucket_mobs/wolf/pale_default" - toast: "simplexity:bucket_mobs/rabbit/toast" # Currently does not work. \ No newline at end of file From 46ac9c625bca774bcc93e5e3745610fd9c6df594 Mon Sep 17 00:00:00 2001 From: Rhythmic System Date: Wed, 30 Jul 2025 19:09:14 -0700 Subject: [PATCH 07/11] Refactored Config - Config should now be more dynamic - default rules for bucketing mobs - Moved style configs under 'bucket-style' - Adjusted handling to use new formats --- .../simplebucketmobs/SimpleBucketMobs.java | 34 +++--- .../command/subcommand/Debucket.java | 4 +- .../simplebucketmobs/config/Config.java | 92 --------------- .../config/ConfigHandler.java | 109 ++++++++++++++++++ .../listener/BucketHandler.java | 8 +- .../simplebucketmobs/listener/BucketRule.java | 38 ++++++ .../listener/EntityHandler.java | 32 ++--- ...{BucketMob.java => InteractListeners.java} | 36 ++++-- .../util/BucketMobPermission.java | 9 +- src/main/resources/config.yml | 47 +++++--- src/main/resources/plugin.yml | 6 - 11 files changed, 252 insertions(+), 163 deletions(-) delete mode 100644 src/main/java/simplexity/simplebucketmobs/config/Config.java create mode 100644 src/main/java/simplexity/simplebucketmobs/config/ConfigHandler.java create mode 100644 src/main/java/simplexity/simplebucketmobs/listener/BucketRule.java rename src/main/java/simplexity/simplebucketmobs/listener/{BucketMob.java => InteractListeners.java} (69%) diff --git a/src/main/java/simplexity/simplebucketmobs/SimpleBucketMobs.java b/src/main/java/simplexity/simplebucketmobs/SimpleBucketMobs.java index 7a9b399..98e49ef 100644 --- a/src/main/java/simplexity/simplebucketmobs/SimpleBucketMobs.java +++ b/src/main/java/simplexity/simplebucketmobs/SimpleBucketMobs.java @@ -1,33 +1,27 @@ package simplexity.simplebucketmobs; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import simplexity.simplebucketmobs.command.CommandHandler; import simplexity.simplebucketmobs.command.subcommand.Debucket; import simplexity.simplebucketmobs.command.subcommand.Reload; -import simplexity.simplebucketmobs.config.Config; +import simplexity.simplebucketmobs.config.ConfigHandler; import simplexity.simplebucketmobs.config.Locale; import simplexity.simplebucketmobs.config.Texture; -import simplexity.simplebucketmobs.listener.BucketMob; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; -import org.bukkit.Bukkit; -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.java.JavaPlugin; +import simplexity.simplebucketmobs.listener.InteractListeners; public final class SimpleBucketMobs extends JavaPlugin { private static Plugin plugin; private static MiniMessage miniMessage; - private static GsonComponentSerializer gsonComponentSerializer; - private static PlainTextComponentSerializer plainTextSerializer; @Override public void onEnable() { plugin = this; miniMessage = MiniMessage.miniMessage(); - gsonComponentSerializer = GsonComponentSerializer.gson(); - plainTextSerializer = PlainTextComponentSerializer.plainText(); - Bukkit.getPluginManager().registerEvents(new BucketMob(), this); + Bukkit.getPluginManager().registerEvents(new InteractListeners(), this); registerCommands(); reloadPluginConfigs(); } @@ -37,17 +31,21 @@ public void onDisable() { // Plugin shutdown logic } - public static Plugin getPlugin() { return plugin; } - public static MiniMessage getMiniMessage() { return miniMessage; } - public static GsonComponentSerializer getGsonSerializer() { return gsonComponentSerializer; } - public static PlainTextComponentSerializer getPlainTextSerializer() { return plainTextSerializer; } + public static Plugin getPlugin() { + return plugin; + } + + public static MiniMessage getMiniMessage() { + return miniMessage; + } public static void reloadPluginConfigs() { plugin.saveDefaultConfig(); - Config.getInstance().reloadConfig(); + ConfigHandler.getInstance().reloadConfig(); Locale.getInstance().reloadLocale(); Texture.getInstance().reloadTextureConfig(); } + private void registerCommands() { this.getCommand("simplebucketmobs").setExecutor(new CommandHandler()); CommandHandler.subcommandList.clear(); diff --git a/src/main/java/simplexity/simplebucketmobs/command/subcommand/Debucket.java b/src/main/java/simplexity/simplebucketmobs/command/subcommand/Debucket.java index 8c5f193..926c0e5 100644 --- a/src/main/java/simplexity/simplebucketmobs/command/subcommand/Debucket.java +++ b/src/main/java/simplexity/simplebucketmobs/command/subcommand/Debucket.java @@ -1,7 +1,7 @@ package simplexity.simplebucketmobs.command.subcommand; import simplexity.simplebucketmobs.command.SubCommand; -import simplexity.simplebucketmobs.listener.BucketMob; +import simplexity.simplebucketmobs.listener.InteractListeners; import simplexity.simplebucketmobs.util.Message; import simplexity.simplebucketmobs.util.BucketMobPermission; import org.bukkit.command.CommandSender; @@ -30,7 +30,7 @@ public void execute(CommandSender sender, String[] args) { } ItemStack item = player.getInventory().getItem(EquipmentSlot.HAND); PersistentDataContainer pdc = item.getItemMeta().getPersistentDataContainer(); - String nbt = pdc.get(BucketMob.legacyMobTag, PersistentDataType.STRING); + String nbt = pdc.get(InteractListeners.legacyMobTag, PersistentDataType.STRING); if (nbt == null) { player.sendMessage(Message.ERROR_NO_BUCKET_MOB.getParsedMessage()); return; diff --git a/src/main/java/simplexity/simplebucketmobs/config/Config.java b/src/main/java/simplexity/simplebucketmobs/config/Config.java deleted file mode 100644 index e007fde..0000000 --- a/src/main/java/simplexity/simplebucketmobs/config/Config.java +++ /dev/null @@ -1,92 +0,0 @@ -package simplexity.simplebucketmobs.config; - -import simplexity.simplebucketmobs.SimpleBucketMobs; -import simplexity.simplebucketmobs.util.Message; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.entity.EntityType; - -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class Config { - - private static Config instance; - - // These can already be bucketed, we don't wanna double-bucket lol - private final Set generalDisallowedTypes = Set.of( - EntityType.TROPICAL_FISH, EntityType.SALMON, EntityType.COD, - EntityType.AXOLOTL, EntityType.PUFFERFISH, EntityType.TADPOLE); - - private final Set allowedBasicTypes; - private String bucketTitle; - private boolean noHostileTargeting; - private boolean listIsBlacklist; - private boolean useResourcePack; - // TODO: Disallowed Attributes - - private Config() { - allowedBasicTypes = new HashSet<>(); - } - - public static Config getInstance() { - if (instance == null) instance = new Config(); - return instance; - } - - public Set getAllowedBasicTypes() { - return Collections.unmodifiableSet(allowedBasicTypes); - } - - public String getBucketTitle() { - return bucketTitle; - } - - public boolean isNoHostileTargeting() { - return noHostileTargeting; - } - - public void reloadConfig() { - SimpleBucketMobs.getPlugin().reloadConfig(); - FileConfiguration config = SimpleBucketMobs.getPlugin().getConfig(); - bucketTitle = config.getString("bucket-title", " Bucket"); - noHostileTargeting = config.getBoolean("no-hostile-targeting", true); - listIsBlacklist = config.getBoolean("list-is-blacklist", false); - useResourcePack = config.getBoolean("use-resource-pack", true); - setupTypes(config); - } - - private void setupTypes(FileConfiguration config) { - List basicTypes = config.getStringList("allowed-types"); - allowedBasicTypes.clear(); - if (listIsBlacklist) { - populateTypes(basicTypes, allowedBasicTypes); - } else { - validateTypes(basicTypes, allowedBasicTypes); - } - } - - private void validateTypes(List stringList, Set entityList) { - for (String type : stringList) { - try { - EntityType entityType = EntityType.valueOf(type.toUpperCase()); - entityList.add(entityType); - } catch (IllegalArgumentException e) { - SimpleBucketMobs.getPlugin().getLogger().warning(Message.LOGGER_INVALID_MOB_TYPE.getMessage() + type); - } - } - } - - private void populateTypes(List stringList, Set entityList) { - for (EntityType entityType : EntityType.values()) { - if (stringList.contains(entityType.toString())) continue; - if (generalDisallowedTypes.contains(entityType)) continue; - entityList.add(entityType); - } - } - - public boolean isUseResourcePack() { - return useResourcePack; - } -} diff --git a/src/main/java/simplexity/simplebucketmobs/config/ConfigHandler.java b/src/main/java/simplexity/simplebucketmobs/config/ConfigHandler.java new file mode 100644 index 0000000..26fa2cd --- /dev/null +++ b/src/main/java/simplexity/simplebucketmobs/config/ConfigHandler.java @@ -0,0 +1,109 @@ +package simplexity.simplebucketmobs.config; + +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.EntityType; +import simplexity.simplebucketmobs.SimpleBucketMobs; +import simplexity.simplebucketmobs.listener.BucketRule; +import simplexity.simplebucketmobs.util.Message; + +import java.util.HashMap; +import java.util.Set; + +public class ConfigHandler { + + private static ConfigHandler instance; + + // These can already be bucketed, we don't wanna double-bucket lol + private final Set generalDisallowedTypes = Set.of( + EntityType.TROPICAL_FISH, EntityType.SALMON, EntityType.COD, + EntityType.AXOLOTL, EntityType.PUFFERFISH, EntityType.TADPOLE); + + private final HashMap entityRules = new HashMap<>(); + private BucketRule defaultRule; + private String bucketTitle; + private boolean useResourcePack, enchantmentGlint; + // TODO: Disallowed Attributes + + public static ConfigHandler getInstance() { + if (instance == null) instance = new ConfigHandler(); + return instance; + } + + public String getBucketTitle() { + return bucketTitle; + } + + public void reloadConfig() { + SimpleBucketMobs.getPlugin().reloadConfig(); + FileConfiguration config = SimpleBucketMobs.getPlugin().getConfig(); + bucketTitle = config.getString("bucket-style.title", " in a Bucket"); + enchantmentGlint = config.getBoolean("bucket-style.enchantment-glint", true); + useResourcePack = config.getBoolean("bucket-style.use-resource-pack", true); + setupTypes(config); + } + + private void setupTypes(FileConfiguration config) { + boolean defaultAllowed = config.getBoolean("bucket-type-settings.default.allow", false); + boolean defaultSneak = config.getBoolean("bucket-type-settings.default.sneak-required", false); + boolean defaultPickupAggro = config.getBoolean("bucket-type-settings.default.pickup-when-aggro", false); + boolean defaultRequiresPermission = config.getBoolean("bucket-type-settings.default.requires-permission", false); + defaultRule = new BucketRule(defaultAllowed, defaultSneak, defaultPickupAggro, defaultRequiresPermission); + ConfigurationSection typesSection = config.getConfigurationSection("bucket-type-settings.types"); + if (typesSection == null) return; + for (EntityType disallowedEntity : generalDisallowedTypes) { + entityRules.put(disallowedEntity, new BucketRule()); + } + entityRules.clear(); + for (String entityKey : typesSection.getKeys(false)) { + EntityType entityType = validateType(entityKey); + if (entityType == null) continue; + if (generalDisallowedTypes.contains(entityType)) continue; + ConfigurationSection entitySection = typesSection.getConfigurationSection(entityKey); + if (entitySection == null) { + entityRules.put(entityType, new BucketRule(defaultAllowed, defaultSneak, defaultPickupAggro, defaultRequiresPermission)); + continue; + } + boolean allow = entitySection.getBoolean("allow", defaultAllowed); + boolean sneak = entitySection.getBoolean("sneak-required", defaultSneak); + boolean aggro = entitySection.getBoolean("pickup-when-aggro", defaultPickupAggro); + boolean reqPerm = entitySection.getBoolean("requires-permission", defaultRequiresPermission); + entityRules.put(entityType, new BucketRule(allow, sneak, aggro, reqPerm)); + } + } + + + private EntityType validateType(String entityName) { + try { + return EntityType.valueOf(entityName.toUpperCase()); + } catch (IllegalArgumentException e) { + SimpleBucketMobs.getPlugin().getLogger().warning(Message.LOGGER_INVALID_MOB_TYPE.getMessage() + entityName); + } + return null; + } + + + public boolean isUsingResourcePack() { + return useResourcePack; + } + + private BucketRule getRule(EntityType type) { + return entityRules.getOrDefault(type, defaultRule); + } + + public boolean bucketingAllowed(EntityType type){ + return getRule(type).isAllowed(); + } + + public boolean isSneakRequired(EntityType type){ + return getRule(type).isSneakRequired(); + } + + public boolean canPickupWhenAggro(EntityType type){ + return getRule(type).canPickupWhenAggro(); + } + + public boolean requiresExplicitPermission(EntityType type){ + return getRule(type).shouldRequirePermission(); + } +} diff --git a/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java b/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java index c0084a7..15d475c 100644 --- a/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java +++ b/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java @@ -13,7 +13,7 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataType; import simplexity.simplebucketmobs.SimpleBucketMobs; -import simplexity.simplebucketmobs.config.Config; +import simplexity.simplebucketmobs.config.ConfigHandler; import simplexity.simplebucketmobs.config.Texture; import java.util.HashMap; @@ -30,7 +30,7 @@ public static ItemStack getMobBucket(LivingEntity entity) { bucketStack.setData(DataComponentTypes.CUSTOM_NAME, nameComponent); bucketStack.setData(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, true); byte[] serializedEntity = Bukkit.getUnsafe().serializeEntity(entity); - if (Config.getInstance().isUseResourcePack()) { + if (ConfigHandler.getInstance().isUsingResourcePack()) { String itemModelString = Texture.getInstance().getItemModel(entity); if (itemModelString == null || !itemModelString.contains(":")) { @@ -48,7 +48,7 @@ public static ItemStack getMobBucket(LivingEntity entity) { } } } - bucketStack.editPersistentDataContainer(pdc -> pdc.set(BucketMob.newMobTag, PersistentDataType.BYTE_ARRAY, serializedEntity)); + bucketStack.editPersistentDataContainer(pdc -> pdc.set(InteractListeners.newMobTag, PersistentDataType.BYTE_ARRAY, serializedEntity)); return bucketStack; } @@ -69,7 +69,7 @@ private static Component getBucketName(LivingEntity entity) { if (entityName == null) entityName = entity.name(); String typeName = entity.getType().name(); String typeNameCased = nameCase(typeName); - String configName = Config.getInstance().getBucketTitle(); + String configName = ConfigHandler.getInstance().getBucketTitle(); return miniMessage.deserialize(configName, Placeholder.component("display_name", entityName), Placeholder.parsed("type", entity.getType().name()), diff --git a/src/main/java/simplexity/simplebucketmobs/listener/BucketRule.java b/src/main/java/simplexity/simplebucketmobs/listener/BucketRule.java new file mode 100644 index 0000000..4fe2c0b --- /dev/null +++ b/src/main/java/simplexity/simplebucketmobs/listener/BucketRule.java @@ -0,0 +1,38 @@ +package simplexity.simplebucketmobs.listener; + +public class BucketRule { + private final boolean allow; + private final boolean sneakRequired; + private final boolean pickupWhenAggro; + private final boolean requiresPermission; + + public BucketRule(){ + this.allow = false; + this.sneakRequired = false; + this.pickupWhenAggro = false; + this.requiresPermission = false; + } + + public BucketRule(boolean allow, boolean sneakRequired, boolean pickupWhenAggro, boolean requiresPermission){ + this.allow = allow; + this.sneakRequired = sneakRequired; + this.pickupWhenAggro = pickupWhenAggro; + this.requiresPermission = requiresPermission; + } + + public boolean isAllowed() { + return allow; + } + + public boolean isSneakRequired() { + return sneakRequired; + } + + public boolean canPickupWhenAggro() { + return pickupWhenAggro; + } + + public boolean shouldRequirePermission() { + return requiresPermission; + } +} diff --git a/src/main/java/simplexity/simplebucketmobs/listener/EntityHandler.java b/src/main/java/simplexity/simplebucketmobs/listener/EntityHandler.java index 2ccf6c1..a3fc9a5 100644 --- a/src/main/java/simplexity/simplebucketmobs/listener/EntityHandler.java +++ b/src/main/java/simplexity/simplebucketmobs/listener/EntityHandler.java @@ -18,28 +18,32 @@ public class EntityHandler { private static final Logger logger = SimpleBucketMobs.getPlugin().getSLF4JLogger(); - public static void handleMobSpawn(Player player, ItemStack itemStack, Block block){ - byte[] entityBytes = itemStack.getPersistentDataContainer().get(BucketMob.newMobTag, PersistentDataType.BYTE_ARRAY); + public static void handleMobSpawn(Player player, ItemStack itemStack, Block block) { + byte[] entityBytes = itemStack.getPersistentDataContainer().get(InteractListeners.newMobTag, PersistentDataType.BYTE_ARRAY); if (entityBytes == null) return; Entity deserializedEntity = Bukkit.getUnsafe().deserializeEntity(entityBytes, block.getWorld(), false, false); if (!(deserializedEntity instanceof LivingEntity livingEntity)) { logger.warn("Entity saved in this bucket was not a living entity, idk how you managed to do that but it's not gonna spawn. Entity info: {}", deserializedEntity); return; } - Location location = block.getLocation().toCenterLocation(); - if (!block.isPassable()) location = location.add(0, 1, 0); - livingEntity.teleport(location); - location.getWorld().addEntity(livingEntity); - player.getInventory().setItemInMainHand(new ItemStack(Material.BUCKET)); + Bukkit.getScheduler().runTaskLater(SimpleBucketMobs.getPlugin(), () -> { + Location location = block.getLocation().toCenterLocation(); + if (!block.isPassable()) location = location.add(0, 1, 0); + livingEntity.teleport(location); + location.getWorld().addEntity(livingEntity); + player.getInventory().setItemInMainHand(new ItemStack(Material.BUCKET)); + }, 2L); } - public static void handleLegacyMobSpawn(Player player, ItemStack itemStack, Block block){ - String nbt = itemStack.getPersistentDataContainer().get(BucketMob.legacyMobTag, PersistentDataType.STRING); + public static void handleLegacyMobSpawn(Player player, ItemStack itemStack, Block block) { + String nbt = itemStack.getPersistentDataContainer().get(InteractListeners.legacyMobTag, PersistentDataType.STRING); if (nbt == null || nbt.isEmpty()) return; - Location location = block.getLocation().toCenterLocation(); - if (!block.isPassable()) location = location.add(0, 1, 0); - EntitySnapshot entitySnapshot = Bukkit.getEntityFactory().createEntitySnapshot(nbt); - entitySnapshot.createEntity(location); - player.getInventory().setItemInMainHand(new ItemStack(Material.BUCKET)); + Bukkit.getScheduler().runTaskLater(SimpleBucketMobs.getPlugin(), () -> { + Location location = block.getLocation().toCenterLocation(); + if (!block.isPassable()) location = location.add(0, 1, 0); + EntitySnapshot entitySnapshot = Bukkit.getEntityFactory().createEntitySnapshot(nbt); + entitySnapshot.createEntity(location); + player.getInventory().setItemInMainHand(new ItemStack(Material.BUCKET)); + }, 2L); } } diff --git a/src/main/java/simplexity/simplebucketmobs/listener/BucketMob.java b/src/main/java/simplexity/simplebucketmobs/listener/InteractListeners.java similarity index 69% rename from src/main/java/simplexity/simplebucketmobs/listener/BucketMob.java rename to src/main/java/simplexity/simplebucketmobs/listener/InteractListeners.java index 8e23220..b8e620e 100644 --- a/src/main/java/simplexity/simplebucketmobs/listener/BucketMob.java +++ b/src/main/java/simplexity/simplebucketmobs/listener/InteractListeners.java @@ -12,45 +12,47 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerBucketFillEvent; import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import simplexity.simplebucketmobs.SimpleBucketMobs; -import simplexity.simplebucketmobs.config.Config; +import simplexity.simplebucketmobs.config.ConfigHandler; import simplexity.simplebucketmobs.util.BucketMobPermission; import simplexity.simplebucketmobs.util.Message; -public class BucketMob implements Listener { +public class InteractListeners implements Listener { public static final NamespacedKey legacyMobTag = new NamespacedKey(SimpleBucketMobs.getPlugin(), "mob_nbt"); public static final NamespacedKey newMobTag = new NamespacedKey(SimpleBucketMobs.getPlugin(), "serialized_mob_data"); - - @EventHandler + @EventHandler(ignoreCancelled = true) public void onBucketMob(PlayerInteractEntityEvent interactEvent) { Entity entity = interactEvent.getRightClicked(); Player player = interactEvent.getPlayer(); if (!(entity instanceof LivingEntity livingEntity)) return; EntityType type = livingEntity.getType(); + if (!ConfigHandler.getInstance().bucketingAllowed(type)) return; + if (ConfigHandler.getInstance().isSneakRequired(type) && !player.isSneaking()) return; + if (ConfigHandler.getInstance().requiresExplicitPermission(type) && + !player.hasPermission(BucketMobPermission.getBucketMobPermission(type))) return; if (livingEntity instanceof Monster monster) { LivingEntity target = monster.getTarget(); - if (target != null && Config.getInstance().isNoHostileTargeting() && monster.isAggressive() && target.equals(player)) { + if (target != null && !ConfigHandler.getInstance().canPickupWhenAggro(type) + && monster.isAggressive() && target.equals(player)) { player.sendRichMessage(Message.ERROR_BUCKET_HOSTILE_TARGETING.getMessage(), Placeholder.parsed("prefix", Message.PREFIX.getMessage())); return; } } - if (!Config.getInstance().getAllowedBasicTypes().contains(type)) return; - if (!(player.hasPermission(BucketMobPermission.BUCKET_MOB_BASE + type.toString().toLowerCase()) || player.hasPermission(BucketMobPermission.BUCKET_ALL))) - return; - if (player.isSneaking()) return; ItemStack itemInHand = player.getInventory().getItemInMainHand(); if (!itemInHand.getType().equals(Material.BUCKET)) return; PersistentDataContainerView bucketPdcView = itemInHand.getPersistentDataContainer(); if (bucketPdcView.has(legacyMobTag) || bucketPdcView.has(newMobTag)) return; ItemStack bucketItem = BucketHandler.getMobBucket(livingEntity); BucketHandler.addMobBucketToInventory(player, bucketItem); - livingEntity.remove(); interactEvent.setCancelled(true); + livingEntity.remove(); } @EventHandler(ignoreCancelled = true) @@ -68,4 +70,18 @@ public void onUnbucketMob(PlayerInteractEvent interactEvent) { if (bucketPdc.has(newMobTag)) EntityHandler.handleMobSpawn(player, itemStack, block); if (bucketPdc.has(legacyMobTag)) EntityHandler.handleLegacyMobSpawn(player, itemStack, block); } + + @EventHandler(ignoreCancelled = true) + public void onBlockBucketEvent(PlayerBucketFillEvent bucketFillEvent){ + EquipmentSlot handUsed = bucketFillEvent.getHand(); + ItemStack itemUsed; + if (handUsed.equals(EquipmentSlot.HAND)) { + itemUsed = bucketFillEvent.getPlayer().getInventory().getItemInMainHand(); + } else { + itemUsed = bucketFillEvent.getPlayer().getInventory().getItemInOffHand(); + } + if (itemUsed.getPersistentDataContainer().has(legacyMobTag) || + itemUsed.getPersistentDataContainer().has(newMobTag)) bucketFillEvent.setCancelled(true); + + } } diff --git a/src/main/java/simplexity/simplebucketmobs/util/BucketMobPermission.java b/src/main/java/simplexity/simplebucketmobs/util/BucketMobPermission.java index 109a85d..115e131 100644 --- a/src/main/java/simplexity/simplebucketmobs/util/BucketMobPermission.java +++ b/src/main/java/simplexity/simplebucketmobs/util/BucketMobPermission.java @@ -1,5 +1,6 @@ package simplexity.simplebucketmobs.util; +import org.bukkit.entity.EntityType; import org.bukkit.permissions.Permission; import org.bukkit.permissions.PermissionDefault; @@ -8,6 +9,10 @@ public class BucketMobPermission { public static final Permission DEBUCKET_COMMAND = new Permission("simplebucketmobs.debucket", "Dump saved mob NBT data from Mob Bucket to chat.", PermissionDefault.OP); public static final Permission COMMAND_RELOAD = new Permission("simplebucketmobs.reload", "Reload plugin configuration files.", PermissionDefault.OP); - public static final Permission BUCKET_MOB_BASE = new Permission("simplebucketmobs.bucket.", "Allows the user to bucket the specified mob.", PermissionDefault.OP); - public static final Permission BUCKET_ALL = new Permission("simplebucketmobs.bucket.all", "Allows the user to bucket all enabled mobs.", PermissionDefault.OP); + public static final Permission BUCKET_MOB_BASE = new Permission("simplebucketmobs.use", "Allows the user to bucket the specified mob.", PermissionDefault.TRUE); + + public static Permission getBucketMobPermission(EntityType type){ + String permName = BUCKET_MOB_BASE + "." + type.toString().toLowerCase(); + return new Permission(permName, PermissionDefault.OP); + } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 7a36628..2d7066a 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,21 +1,38 @@ # : Returns the literal type of the entity (ie: SHEEP) # : Returns the literal type of the entity but not screaming (ie: Sheep) # : Returns the display name or the type_name_cased if it is not named (ie: _jeb) -bucket-title: " in a Bucket" +bucket-style: + title: " in a Bucket" + enchantment-glint: true + use-resource-pack: true + default-item-model: "minecraft:bucket" + # Whether the plugin should set the textures to the ones specified in texture.yml, defaults are from # https://modrinth.com/resourcepack/bucket-mobs-resources/versions -use-resource-pack: true - -# Hostile mobs cannot be bucketed if they are targeting you. -no-hostile-targeting: true -# The allowed types of entities that the bucket can be used on. +# The settings for the bucket-able mobs, or non-bucketable mobs. Fields left empty or non-existent will fall back to default +# And if there is not a default, will default to 'false' # List of Entity Types here: https://purpurmc.org/javadoc/org/bukkit/entity/EntityType.html -# Only LivingEntity types can be used (unless it's an Armor Stand / Player, it will not work). -# Fun fact: Armor Stand is a "Living Entity". -allowed-types: - - SHEEP - - COW - - PIG -# If true, the above list will be the types that are not allowed to be picked up. -# Note, mobs that can already be picked up with a bucket are disabled by default. -list-is-blacklist: false \ No newline at end of file +bucket-type-settings: + default: + allow: false + sneak-required: false + pickup-when-aggro: false + requires-permission: false + types: + VILLAGER: + allow: true + sneak-required: true + requires-permission: true + SLIME: + allow: true + pickup-when-aggro: true + SHEEP: + allow: true + COW: + allow: true + sneak-required: true + PIG: + allow: true + MOOSHROOM: + allow: true + sneak-required: true diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index c7ec51d..593d398 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -10,12 +10,6 @@ commands: description: "SimpleBucketMobs Base Command." aliases: ["sbm", "simplebucketmob"] permissions: - simplebucketmobs.bucket.: - description: "Allows the user to bucket the specified mob." - default: false - simplebucketmobs.bucket.all: - description: "Allows the user to bucket all enabled mobs." - default: op simplebucketmobs.reload: description: "Reload plugin configuration files." default: op From e5e105d67fe3326f6856a0307dacb4090d51af5c Mon Sep 17 00:00:00 2001 From: Rhythmic System Date: Thu, 31 Jul 2025 14:49:23 -0700 Subject: [PATCH 08/11] Adjusted item model stuff - getItemModel now returns the actual key - validation of the string happens in the Texture class now - Started work on making capture items and captured items more customizable --- pom.xml | 2 +- .../config/ConfigHandler.java | 54 ++++++++++++++++--- .../simplebucketmobs/config/Locale.java | 2 +- .../simplebucketmobs/config/Texture.java | 15 +++++- .../listener/BucketHandler.java | 22 ++------ .../listener/CaptureItem.java | 43 +++++++++++++++ .../listener/CapturedItem.java | 42 +++++++++++++++ .../listener/EntityHandler.java | 11 ++++ .../listener/InteractListeners.java | 3 +- .../simplebucketmobs/util/Message.java | 3 -- src/main/resources/config.yml | 23 ++++++-- 11 files changed, 182 insertions(+), 38 deletions(-) create mode 100644 src/main/java/simplexity/simplebucketmobs/listener/CaptureItem.java create mode 100644 src/main/java/simplexity/simplebucketmobs/listener/CapturedItem.java diff --git a/pom.xml b/pom.xml index cfc1624..7fa647c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.simplexity SimpleBucketMobs - 1.3.0 + 2.0.0-DEV jar SimpleBucketMobs diff --git a/src/main/java/simplexity/simplebucketmobs/config/ConfigHandler.java b/src/main/java/simplexity/simplebucketmobs/config/ConfigHandler.java index 26fa2cd..cd8e7dd 100644 --- a/src/main/java/simplexity/simplebucketmobs/config/ConfigHandler.java +++ b/src/main/java/simplexity/simplebucketmobs/config/ConfigHandler.java @@ -1,11 +1,13 @@ package simplexity.simplebucketmobs.config; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.EntityType; +import org.slf4j.Logger; import simplexity.simplebucketmobs.SimpleBucketMobs; import simplexity.simplebucketmobs.listener.BucketRule; -import simplexity.simplebucketmobs.util.Message; import java.util.HashMap; import java.util.Set; @@ -13,6 +15,7 @@ public class ConfigHandler { private static ConfigHandler instance; + private static final Logger logger = SimpleBucketMobs.getPlugin().getSLF4JLogger(); // These can already be bucketed, we don't wanna double-bucket lol private final Set generalDisallowedTypes = Set.of( @@ -22,7 +25,9 @@ public class ConfigHandler { private final HashMap entityRules = new HashMap<>(); private BucketRule defaultRule; private String bucketTitle; + private NamespacedKey defaultModel; private boolean useResourcePack, enchantmentGlint; + private Material mobBucketMaterial; // TODO: Disallowed Attributes public static ConfigHandler getInstance() { @@ -40,6 +45,8 @@ public void reloadConfig() { bucketTitle = config.getString("bucket-style.title", " in a Bucket"); enchantmentGlint = config.getBoolean("bucket-style.enchantment-glint", true); useResourcePack = config.getBoolean("bucket-style.use-resource-pack", true); + mobBucketMaterial = validateMaterial(config.getString("bucket-style.item-type"), Material.BUCKET, "bucket-style.item-type"); + defaultModel = validateItemModel(config.getString("bucket-style.default-item-model", "minecraft:bucket")); setupTypes(config); } @@ -77,11 +84,34 @@ private EntityType validateType(String entityName) { try { return EntityType.valueOf(entityName.toUpperCase()); } catch (IllegalArgumentException e) { - SimpleBucketMobs.getPlugin().getLogger().warning(Message.LOGGER_INVALID_MOB_TYPE.getMessage() + entityName); + logger.warn("Invalid entity type: {} - settings for this entity will be skipped", entityName); } return null; } + private NamespacedKey validateItemModel(String modelLocation){ + NamespacedKey key = NamespacedKey.fromString(modelLocation); + if (key == null) { + logger.warn("Invalid item model: {} - using default model 'minecraft:bucket'", modelLocation); + return NamespacedKey.fromString("minecraft:bucket"); + } + return key; + } + + @SuppressWarnings("SameParameterValue") + private Material validateMaterial(String materialName, Material defaultMaterial, String path) { + if (materialName == null) { + logger.warn("No material found for {}, using default material: {}", path, defaultMaterial); + return defaultMaterial; + } + Material material = Material.getMaterial(materialName); + if (material == null) { + logger.warn("Invalid material in '{}': {} - using default material: {}", path, materialName, defaultMaterial); + return defaultMaterial; + } + return material; + } + public boolean isUsingResourcePack() { return useResourcePack; @@ -91,19 +121,31 @@ private BucketRule getRule(EntityType type) { return entityRules.getOrDefault(type, defaultRule); } - public boolean bucketingAllowed(EntityType type){ + public boolean bucketingAllowed(EntityType type) { return getRule(type).isAllowed(); } - public boolean isSneakRequired(EntityType type){ + public boolean isSneakRequired(EntityType type) { return getRule(type).isSneakRequired(); } - public boolean canPickupWhenAggro(EntityType type){ + public boolean canPickupWhenAggro(EntityType type) { return getRule(type).canPickupWhenAggro(); } - public boolean requiresExplicitPermission(EntityType type){ + public boolean requiresExplicitPermission(EntityType type) { return getRule(type).shouldRequirePermission(); } + + public boolean isEnchantmentGlint() { + return enchantmentGlint; + } + + public Material getMobBucketMaterial() { + return mobBucketMaterial; + } + + public NamespacedKey getDefaultModel() { + return defaultModel; + } } diff --git a/src/main/java/simplexity/simplebucketmobs/config/Locale.java b/src/main/java/simplexity/simplebucketmobs/config/Locale.java index d0ad5df..9233af6 100644 --- a/src/main/java/simplexity/simplebucketmobs/config/Locale.java +++ b/src/main/java/simplexity/simplebucketmobs/config/Locale.java @@ -39,7 +39,7 @@ public void reloadLocale() { Message message = Message.valueOf(key); message.setMessage(locale.getString(key, message.getMessage())); } catch (IllegalArgumentException e) { - SimpleBucketMobs.getPlugin().getLogger().warning(Message.LOGGER_INVALID_LOCALE_KEY + key); + SimpleBucketMobs.getPlugin().getSLF4JLogger().warn("Invalid locale key: {}", key); } } } diff --git a/src/main/java/simplexity/simplebucketmobs/config/Texture.java b/src/main/java/simplexity/simplebucketmobs/config/Texture.java index 257a396..b157b4d 100644 --- a/src/main/java/simplexity/simplebucketmobs/config/Texture.java +++ b/src/main/java/simplexity/simplebucketmobs/config/Texture.java @@ -1,6 +1,7 @@ package simplexity.simplebucketmobs.config; import org.bukkit.DyeColor; +import org.bukkit.NamespacedKey; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; @@ -21,6 +22,7 @@ import org.bukkit.entity.TraderLlama; import org.bukkit.entity.Villager; import org.bukkit.entity.Wolf; +import org.slf4j.Logger; import simplexity.simplebucketmobs.SimpleBucketMobs; import java.io.File; @@ -34,6 +36,7 @@ public class Texture { private final String fileName = "texture.yml"; private final File dataFile = new File(SimpleBucketMobs.getPlugin().getDataFolder(), fileName); private final FileConfiguration texture = new YamlConfiguration(); + private final Logger logger = SimpleBucketMobs.getPlugin().getSLF4JLogger(); private Texture() { if (!dataFile.exists()) SimpleBucketMobs.getPlugin().saveResource(fileName, false); @@ -57,7 +60,17 @@ public void reloadTextureConfig() { } } - public String getItemModel(LivingEntity entity) { + public NamespacedKey getItemModel(LivingEntity entity) { + String itemModelLocation = locateItemModel(entity); + NamespacedKey key = NamespacedKey.fromString(itemModelLocation); + if (key == null) { + logger.warn("Invalid or missing item model string for entity: {} Value: '{}'", entity.getType(), itemModelLocation); + return ConfigHandler.getInstance().getDefaultModel(); + } + return key; + } + + private String locateItemModel(LivingEntity entity) { if (entity instanceof Cat cat) return getCatItemModel(cat); if (entity instanceof Chicken chicken) return getChickenItemModel(chicken); if (entity instanceof Cow cow) return getCowItemModel(cow); diff --git a/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java b/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java index 15d475c..959237d 100644 --- a/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java +++ b/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java @@ -5,7 +5,6 @@ import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import org.bukkit.Bukkit; -import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; @@ -24,35 +23,20 @@ public class BucketHandler { private static final MiniMessage miniMessage = SimpleBucketMobs.getMiniMessage(); public static ItemStack getMobBucket(LivingEntity entity) { - ItemStack bucketStack = new ItemStack(Material.BUCKET); + ItemStack bucketStack = new ItemStack(ConfigHandler.getInstance().getMobBucketMaterial()); bucketStack.setData(DataComponentTypes.MAX_STACK_SIZE, 1); Component nameComponent = getBucketName(entity); bucketStack.setData(DataComponentTypes.CUSTOM_NAME, nameComponent); bucketStack.setData(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, true); byte[] serializedEntity = Bukkit.getUnsafe().serializeEntity(entity); if (ConfigHandler.getInstance().isUsingResourcePack()) { - String itemModelString = Texture.getInstance().getItemModel(entity); - - if (itemModelString == null || !itemModelString.contains(":")) { - Bukkit.getLogger().warning("Invalid or missing item model string for entity: " + entity.getType() + - ". Value: '" + itemModelString + "'"); - } else { - String[] split = itemModelString.trim().split(":"); - - if (split.length != 2 || split[0].isEmpty() || split[1].isEmpty()) { - Bukkit.getLogger().warning("Malformed item model string for entity: " + entity.getType() + - ". Value: '" + itemModelString + "'"); - } else { - NamespacedKey namespacedKey = new NamespacedKey(split[0], split[1]); - bucketStack.setData(DataComponentTypes.ITEM_MODEL, namespacedKey); - } - } + NamespacedKey modelKey = Texture.getInstance().getItemModel(entity); + bucketStack.setData(DataComponentTypes.ITEM_MODEL, modelKey); } bucketStack.editPersistentDataContainer(pdc -> pdc.set(InteractListeners.newMobTag, PersistentDataType.BYTE_ARRAY, serializedEntity)); return bucketStack; } - public static void addMobBucketToInventory(Player player, ItemStack bucket) { Inventory inventory = player.getInventory(); int amountOfBuckets = player.getInventory().getItemInMainHand().getAmount(); diff --git a/src/main/java/simplexity/simplebucketmobs/listener/CaptureItem.java b/src/main/java/simplexity/simplebucketmobs/listener/CaptureItem.java new file mode 100644 index 0000000..f85b203 --- /dev/null +++ b/src/main/java/simplexity/simplebucketmobs/listener/CaptureItem.java @@ -0,0 +1,43 @@ +package simplexity.simplebucketmobs.listener; + +import org.bukkit.Material; +import org.bukkit.NamespacedKey; + +public class CaptureItem { + + private final String itemName; + private final boolean enchantGlint; + private final NamespacedKey itemModel; + private final Material itemType; + private final Integer stackSize; + + + public CaptureItem(String itemName, boolean enchantGlint, NamespacedKey itemModel, Material itemType, Integer stackSize) { + this.itemName = itemName; + this.enchantGlint = enchantGlint; + this.itemModel = itemModel; + this.itemType = itemType; + this.stackSize = stackSize; + } + + + public String getItemName() { + return itemName; + } + + public boolean isEnchantGlint() { + return enchantGlint; + } + + public NamespacedKey getItemModel() { + return itemModel; + } + + public Material getItemType() { + return itemType; + } + + public Integer getStackSize() { + return stackSize; + } +} diff --git a/src/main/java/simplexity/simplebucketmobs/listener/CapturedItem.java b/src/main/java/simplexity/simplebucketmobs/listener/CapturedItem.java new file mode 100644 index 0000000..491cdde --- /dev/null +++ b/src/main/java/simplexity/simplebucketmobs/listener/CapturedItem.java @@ -0,0 +1,42 @@ +package simplexity.simplebucketmobs.listener; + +import org.bukkit.Material; +import org.bukkit.NamespacedKey; + +public class CapturedItem { + + private final String itemName; + private final String itemLore; + private final boolean enchantGlint; + private final NamespacedKey itemModel; + private final Material itemType; + + + public CapturedItem(String itemName, String itemLore,boolean enchantGlint, NamespacedKey itemModel, Material itemType) { + this.itemName = itemName; + this.itemLore = itemLore; + this.enchantGlint = enchantGlint; + this.itemModel = itemModel; + this.itemType = itemType; + } + + public String getItemName() { + return itemName; + } + + public String getItemLore() { + return itemLore; + } + + public boolean isEnchantGlint() { + return enchantGlint; + } + + public NamespacedKey getItemModel() { + return itemModel; + } + + public Material getItemType() { + return itemType; + } +} diff --git a/src/main/java/simplexity/simplebucketmobs/listener/EntityHandler.java b/src/main/java/simplexity/simplebucketmobs/listener/EntityHandler.java index a3fc9a5..1dd1963 100644 --- a/src/main/java/simplexity/simplebucketmobs/listener/EntityHandler.java +++ b/src/main/java/simplexity/simplebucketmobs/listener/EntityHandler.java @@ -13,11 +13,16 @@ import org.slf4j.Logger; import simplexity.simplebucketmobs.SimpleBucketMobs; +import java.util.ArrayList; +import java.util.List; + @SuppressWarnings({"UnstableApiUsage", "deprecation"}) public class EntityHandler { private static final Logger logger = SimpleBucketMobs.getPlugin().getSLF4JLogger(); + private static final List pleaseStopDuplicatingMobsThanks = new ArrayList<>(); + public static void handleMobSpawn(Player player, ItemStack itemStack, Block block) { byte[] entityBytes = itemStack.getPersistentDataContainer().get(InteractListeners.newMobTag, PersistentDataType.BYTE_ARRAY); if (entityBytes == null) return; @@ -26,24 +31,30 @@ public static void handleMobSpawn(Player player, ItemStack itemStack, Block bloc logger.warn("Entity saved in this bucket was not a living entity, idk how you managed to do that but it's not gonna spawn. Entity info: {}", deserializedEntity); return; } + if (pleaseStopDuplicatingMobsThanks.contains(itemStack)) return; + pleaseStopDuplicatingMobsThanks.add(itemStack); Bukkit.getScheduler().runTaskLater(SimpleBucketMobs.getPlugin(), () -> { Location location = block.getLocation().toCenterLocation(); if (!block.isPassable()) location = location.add(0, 1, 0); livingEntity.teleport(location); location.getWorld().addEntity(livingEntity); player.getInventory().setItemInMainHand(new ItemStack(Material.BUCKET)); + pleaseStopDuplicatingMobsThanks.remove(itemStack); }, 2L); } public static void handleLegacyMobSpawn(Player player, ItemStack itemStack, Block block) { String nbt = itemStack.getPersistentDataContainer().get(InteractListeners.legacyMobTag, PersistentDataType.STRING); if (nbt == null || nbt.isEmpty()) return; + if (pleaseStopDuplicatingMobsThanks.contains(itemStack)) return; + pleaseStopDuplicatingMobsThanks.add(itemStack); Bukkit.getScheduler().runTaskLater(SimpleBucketMobs.getPlugin(), () -> { Location location = block.getLocation().toCenterLocation(); if (!block.isPassable()) location = location.add(0, 1, 0); EntitySnapshot entitySnapshot = Bukkit.getEntityFactory().createEntitySnapshot(nbt); entitySnapshot.createEntity(location); player.getInventory().setItemInMainHand(new ItemStack(Material.BUCKET)); + pleaseStopDuplicatingMobsThanks.remove(itemStack); }, 2L); } } diff --git a/src/main/java/simplexity/simplebucketmobs/listener/InteractListeners.java b/src/main/java/simplexity/simplebucketmobs/listener/InteractListeners.java index b8e620e..20b40c1 100644 --- a/src/main/java/simplexity/simplebucketmobs/listener/InteractListeners.java +++ b/src/main/java/simplexity/simplebucketmobs/listener/InteractListeners.java @@ -64,7 +64,7 @@ public void onUnbucketMob(PlayerInteractEvent interactEvent) { if (interactEvent.getAction().isLeftClick()) return; if (!interactEvent.hasBlock()) return; if (block == null || block.getType().equals(Material.AIR)) return; - if (!itemStack.getType().equals(Material.BUCKET)) return; + if (!(itemStack.getType().equals(Material.BUCKET) || itemStack.getType().equals(ConfigHandler.getInstance().getMobBucketMaterial()))) return; if (!(bucketPdc.has(newMobTag) || bucketPdc.has(legacyMobTag))) return; interactEvent.setCancelled(true); if (bucketPdc.has(newMobTag)) EntityHandler.handleMobSpawn(player, itemStack, block); @@ -82,6 +82,5 @@ public void onBlockBucketEvent(PlayerBucketFillEvent bucketFillEvent){ } if (itemUsed.getPersistentDataContainer().has(legacyMobTag) || itemUsed.getPersistentDataContainer().has(newMobTag)) bucketFillEvent.setCancelled(true); - } } diff --git a/src/main/java/simplexity/simplebucketmobs/util/Message.java b/src/main/java/simplexity/simplebucketmobs/util/Message.java index 341bd9d..d343401 100644 --- a/src/main/java/simplexity/simplebucketmobs/util/Message.java +++ b/src/main/java/simplexity/simplebucketmobs/util/Message.java @@ -8,9 +8,6 @@ public enum Message { PREFIX("[SimpleBucketMobs] » "), - LOGGER_INVALID_LOCALE_KEY("Invalid Key in locale.yml: "), - LOGGER_INVALID_MOB_TYPE("Invalid Mob Type in config.yml: "), - ERROR_NOT_A_PLAYER("This command can only be executed by a player."), ERROR_NO_BUCKET_MOB("This is not a bucket mob, please report this to a server admin."), ERROR_FAILED_DESERIALIZATION("Failed to deserialize mob, please report this to a server admin."), diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 2d7066a..9777f35 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,12 +1,23 @@ # : Returns the literal type of the entity (ie: SHEEP) # : Returns the literal type of the entity but not screaming (ie: Sheep) # : Returns the display name or the type_name_cased if it is not named (ie: _jeb) -bucket-style: - title: " in a Bucket" - enchantment-glint: true - use-resource-pack: true - default-item-model: "minecraft:bucket" +capture-item-style: + default: + name: default + enchantment-glint: false + item-model: default + item-type: BUCKET + stack-size: default +captured-item-style: + use-resource-pack: true + default: + item-type: BUCKET + item-model: default + name: " in a Bucket" + lore: + - "A sits in the bucket" + enchantment-glint: true # Whether the plugin should set the textures to the ones specified in texture.yml, defaults are from # https://modrinth.com/resourcepack/bucket-mobs-resources/versions # The settings for the bucket-able mobs, or non-bucketable mobs. Fields left empty or non-existent will fall back to default @@ -15,6 +26,8 @@ bucket-style: bucket-type-settings: default: allow: false + capture-item: default + captured-item: default sneak-required: false pickup-when-aggro: false requires-permission: false From ff50f2b196bf96d5eb3b39d3815109c26ccc44c5 Mon Sep 17 00:00:00 2001 From: Rhythmic Date: Sat, 18 Apr 2026 23:50:32 -0500 Subject: [PATCH 09/11] Fix config key mismatch, entity spawn location, and several event-handling bugs ConfigHandler was reading from bucket-style.* keys that never existed in config.yml; the correct section is captured-item-style.*. This silently ignored every style setting (name, glint, material, item model, resource pack toggle) and fell back to Java-side defaults. Fixed all key paths. config.yml had item-model: default, which is not a valid NamespacedKey and logged a warning on every reload. Changed to minecraft:bucket. Entity release now uses NMS Entity.setPos() via reflection before addEntity() is called, so mobs spawn at the release location directly. The previous approach (teleport before/after addEntity) broke when the capture chunk was unloaded, because teleport() requires the entity to already be in a world, and addEntity() always used the stored NBT position. Additional fixes: - Enchantment glint config option was always hardcoded to true; now reads isEnchantmentGlint() from ConfigHandler - Dead-code loop in setupTypes() added disallowed types to entityRules then immediately cleared them; removed the loop - onBucketMob and onUnbucketMob now guard on EquipmentSlot.HAND to avoid processing off-hand duplicate events - Event is now cancelled before world mutations in onBucketMob so other listeners do not see a half-removed entity - Debucket command: added null check for empty main hand, and now handles both the legacy string tag and the new byte-array tag - Texture config load error now goes through the SLF4J logger instead of printStackTrace() --- .../command/subcommand/Debucket.java | 17 +++++-- .../config/ConfigHandler.java | 13 ++---- .../simplebucketmobs/config/Texture.java | 29 +++--------- .../listener/BucketHandler.java | 2 +- .../listener/EntityHandler.java | 46 ++++++++++++++++++- .../listener/InteractListeners.java | 11 ++++- src/main/resources/config.yml | 8 ++-- 7 files changed, 83 insertions(+), 43 deletions(-) diff --git a/src/main/java/simplexity/simplebucketmobs/command/subcommand/Debucket.java b/src/main/java/simplexity/simplebucketmobs/command/subcommand/Debucket.java index 926c0e5..5e69199 100644 --- a/src/main/java/simplexity/simplebucketmobs/command/subcommand/Debucket.java +++ b/src/main/java/simplexity/simplebucketmobs/command/subcommand/Debucket.java @@ -29,17 +29,28 @@ public void execute(CommandSender sender, String[] args) { return; } ItemStack item = player.getInventory().getItem(EquipmentSlot.HAND); + if (item == null || item.getType().isAir() || item.getItemMeta() == null) { + player.sendMessage(Message.ERROR_NO_BUCKET_MOB.getParsedMessage()); + return; + } PersistentDataContainer pdc = item.getItemMeta().getPersistentDataContainer(); + // Legacy format: NBT stored as a string String nbt = pdc.get(InteractListeners.legacyMobTag, PersistentDataType.STRING); - if (nbt == null) { + if (nbt != null) { + player.sendMessage(nbt); + return; + } + // New format: entity serialized as a byte array + byte[] serializedData = pdc.get(InteractListeners.newMobTag, PersistentDataType.BYTE_ARRAY); + if (serializedData == null) { player.sendMessage(Message.ERROR_NO_BUCKET_MOB.getParsedMessage()); return; } - player.sendMessage(nbt); + player.sendMessage("Serialized mob data (" + serializedData.length + " bytes, binary format)"); } @Override public List getSubcommandArguments(CommandSender sender, String[] args) { return null; } -} +} \ No newline at end of file diff --git a/src/main/java/simplexity/simplebucketmobs/config/ConfigHandler.java b/src/main/java/simplexity/simplebucketmobs/config/ConfigHandler.java index cd8e7dd..16c46a1 100644 --- a/src/main/java/simplexity/simplebucketmobs/config/ConfigHandler.java +++ b/src/main/java/simplexity/simplebucketmobs/config/ConfigHandler.java @@ -42,11 +42,11 @@ public String getBucketTitle() { public void reloadConfig() { SimpleBucketMobs.getPlugin().reloadConfig(); FileConfiguration config = SimpleBucketMobs.getPlugin().getConfig(); - bucketTitle = config.getString("bucket-style.title", " in a Bucket"); - enchantmentGlint = config.getBoolean("bucket-style.enchantment-glint", true); - useResourcePack = config.getBoolean("bucket-style.use-resource-pack", true); - mobBucketMaterial = validateMaterial(config.getString("bucket-style.item-type"), Material.BUCKET, "bucket-style.item-type"); - defaultModel = validateItemModel(config.getString("bucket-style.default-item-model", "minecraft:bucket")); + bucketTitle = config.getString("captured-item-style.default.name", " in a Bucket"); + enchantmentGlint = config.getBoolean("captured-item-style.default.enchantment-glint", true); + useResourcePack = config.getBoolean("captured-item-style.use-resource-pack", true); + mobBucketMaterial = validateMaterial(config.getString("captured-item-style.default.item-type"), Material.BUCKET, "captured-item-style.default.item-type"); + defaultModel = validateItemModel(config.getString("captured-item-style.default.item-model", "minecraft:bucket")); setupTypes(config); } @@ -58,9 +58,6 @@ private void setupTypes(FileConfiguration config) { defaultRule = new BucketRule(defaultAllowed, defaultSneak, defaultPickupAggro, defaultRequiresPermission); ConfigurationSection typesSection = config.getConfigurationSection("bucket-type-settings.types"); if (typesSection == null) return; - for (EntityType disallowedEntity : generalDisallowedTypes) { - entityRules.put(disallowedEntity, new BucketRule()); - } entityRules.clear(); for (String entityKey : typesSection.getKeys(false)) { EntityType entityType = validateType(entityKey); diff --git a/src/main/java/simplexity/simplebucketmobs/config/Texture.java b/src/main/java/simplexity/simplebucketmobs/config/Texture.java index b157b4d..0dd70af 100644 --- a/src/main/java/simplexity/simplebucketmobs/config/Texture.java +++ b/src/main/java/simplexity/simplebucketmobs/config/Texture.java @@ -5,30 +5,13 @@ import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Cat; -import org.bukkit.entity.Chicken; -import org.bukkit.entity.Cow; -import org.bukkit.entity.Fox; -import org.bukkit.entity.Frog; -import org.bukkit.entity.Horse; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Llama; -import org.bukkit.entity.MushroomCow; -import org.bukkit.entity.Parrot; -import org.bukkit.entity.Pig; -import org.bukkit.entity.Rabbit; -import org.bukkit.entity.Sheep; -import org.bukkit.entity.Shulker; -import org.bukkit.entity.TraderLlama; -import org.bukkit.entity.Villager; -import org.bukkit.entity.Wolf; +import org.bukkit.entity.*; import org.slf4j.Logger; import simplexity.simplebucketmobs.SimpleBucketMobs; import java.io.File; import java.io.IOException; -@SuppressWarnings({"CallToPrintStackTrace"}) public class Texture { private static Texture instance; @@ -56,7 +39,7 @@ public void reloadTextureConfig() { try { texture.load(dataFile); } catch (IOException | InvalidConfigurationException e) { - e.printStackTrace(); + logger.error("Failed to load {}", fileName, e); } } @@ -73,6 +56,7 @@ public NamespacedKey getItemModel(LivingEntity entity) { private String locateItemModel(LivingEntity entity) { if (entity instanceof Cat cat) return getCatItemModel(cat); if (entity instanceof Chicken chicken) return getChickenItemModel(chicken); + if (entity instanceof MushroomCow mooshroom) return getMooshroomItemModel(mooshroom); if (entity instanceof Cow cow) return getCowItemModel(cow); if (entity instanceof Fox fox) return getFoxItemModel(fox); if (entity instanceof Frog frog) return getFrogItemModel(frog); @@ -80,7 +64,6 @@ private String locateItemModel(LivingEntity entity) { // Have to do this first otherwise llama will eat it if (entity instanceof TraderLlama traderLlama) return getTraderLlamaItemModel(traderLlama); if (entity instanceof Llama llama) return getLlamaItemModel(llama); - if (entity instanceof MushroomCow mooshroom) return getMooshroomItemModel(mooshroom); if (entity instanceof Parrot parrot) return getParrotItemModel(parrot); if (entity instanceof Pig pig) return getPigItemModel(pig); if (entity instanceof Rabbit rabbit) return getRabbitItemModel(rabbit); @@ -98,13 +81,13 @@ public String getCatItemModel(Cat cat) { } public String getChickenItemModel(Chicken chicken) { - Chicken.Variant chickenType = chicken.getVariant(); + EntityType chickenType = chicken.getType(); String defaultTexture = texture.getString("chicken.default", "minecraft:bucket"); return texture.getString("chicken.type." + chickenType.toString().toLowerCase(), defaultTexture); } public String getCowItemModel(Cow cow) { - Cow.Variant cowType = cow.getVariant(); + EntityType cowType = cow.getType(); String defaultTexture = texture.getString("cow.default", "minecraft:bucket"); return texture.getString("cow.type." + cowType.toString().toLowerCase(), defaultTexture); } @@ -150,7 +133,7 @@ public String getParrotItemModel(Parrot parrot) { } public String getPigItemModel(Pig pig) { - Pig.Variant pigType = pig.getVariant(); + EntityType pigType = pig.getType(); String defaultTexture = texture.getString("pig.default", "minecraft:bucket"); return texture.getString("pig.type." + pigType.toString().toLowerCase(), defaultTexture); } diff --git a/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java b/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java index 959237d..63fc978 100644 --- a/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java +++ b/src/main/java/simplexity/simplebucketmobs/listener/BucketHandler.java @@ -27,7 +27,7 @@ public static ItemStack getMobBucket(LivingEntity entity) { bucketStack.setData(DataComponentTypes.MAX_STACK_SIZE, 1); Component nameComponent = getBucketName(entity); bucketStack.setData(DataComponentTypes.CUSTOM_NAME, nameComponent); - bucketStack.setData(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, true); + bucketStack.setData(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, ConfigHandler.getInstance().isEnchantmentGlint()); byte[] serializedEntity = Bukkit.getUnsafe().serializeEntity(entity); if (ConfigHandler.getInstance().isUsingResourcePack()) { NamespacedKey modelKey = Texture.getInstance().getItemModel(entity); diff --git a/src/main/java/simplexity/simplebucketmobs/listener/EntityHandler.java b/src/main/java/simplexity/simplebucketmobs/listener/EntityHandler.java index 1dd1963..6407ac2 100644 --- a/src/main/java/simplexity/simplebucketmobs/listener/EntityHandler.java +++ b/src/main/java/simplexity/simplebucketmobs/listener/EntityHandler.java @@ -13,6 +13,7 @@ import org.slf4j.Logger; import simplexity.simplebucketmobs.SimpleBucketMobs; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; @@ -23,6 +24,23 @@ public class EntityHandler { private static final List pleaseStopDuplicatingMobsThanks = new ArrayList<>(); + // Cached reflection handles for NMS position setting (Paper 1.21+ Mojang mappings) + private static Method getHandleMethod; + private static Method setPosMethod; + + static { + try { + // CraftEntity.getHandle() is public and accessible via reflection + Class craftEntityClass = Class.forName("org.bukkit.craftbukkit.entity.CraftEntity"); + getHandleMethod = craftEntityClass.getMethod("getHandle"); + // net.minecraft.world.entity.Entity.setPos(double x, double y, double z) + Class nmsEntityClass = Class.forName("net.minecraft.world.entity.Entity"); + setPosMethod = nmsEntityClass.getMethod("setPos", double.class, double.class, double.class); + } catch (Exception e) { + logger.warn("Could not cache NMS position methods; mob release will fall back to teleport", e); + } + } + public static void handleMobSpawn(Player player, ItemStack itemStack, Block block) { byte[] entityBytes = itemStack.getPersistentDataContainer().get(InteractListeners.newMobTag, PersistentDataType.BYTE_ARRAY); if (entityBytes == null) return; @@ -36,7 +54,11 @@ public static void handleMobSpawn(Player player, ItemStack itemStack, Block bloc Bukkit.getScheduler().runTaskLater(SimpleBucketMobs.getPlugin(), () -> { Location location = block.getLocation().toCenterLocation(); if (!block.isPassable()) location = location.add(0, 1, 0); - livingEntity.teleport(location); + // Set the NMS position before addEntity() so the entity spawns directly at + // the release location rather than its stored (capture-time) position. + // This avoids force-loading the capture chunk and prevents teleport failures + // when that chunk is unloaded. + setNmsPosition(livingEntity, location); location.getWorld().addEntity(livingEntity); player.getInventory().setItemInMainHand(new ItemStack(Material.BUCKET)); pleaseStopDuplicatingMobsThanks.remove(itemStack); @@ -57,4 +79,24 @@ public static void handleLegacyMobSpawn(Player player, ItemStack itemStack, Bloc pleaseStopDuplicatingMobsThanks.remove(itemStack); }, 2L); } -} + + /** + * Sets the NMS entity's position directly before it is added to a world. + * {@code Entity.teleport()} requires the entity to already be in a world, so we reach + * into the NMS layer via reflection to update the position fields while the entity is + * still unspawned. Paper 1.21+ uses Mojang mappings at runtime, so the method name + * {@code setPos} is stable. + */ + private static void setNmsPosition(Entity entity, Location location) { + if (getHandleMethod == null || setPosMethod == null) { + logger.warn("NMS position methods unavailable; mob may spawn at its capture location"); + return; + } + try { + Object nmsEntity = getHandleMethod.invoke(entity); + setPosMethod.invoke(nmsEntity, location.getX(), location.getY(), location.getZ()); + } catch (Exception e) { + logger.warn("Failed to set NMS entity position before spawn", e); + } + } +} \ No newline at end of file diff --git a/src/main/java/simplexity/simplebucketmobs/listener/InteractListeners.java b/src/main/java/simplexity/simplebucketmobs/listener/InteractListeners.java index 20b40c1..3c5ecae 100644 --- a/src/main/java/simplexity/simplebucketmobs/listener/InteractListeners.java +++ b/src/main/java/simplexity/simplebucketmobs/listener/InteractListeners.java @@ -26,8 +26,11 @@ public class InteractListeners implements Listener { public static final NamespacedKey legacyMobTag = new NamespacedKey(SimpleBucketMobs.getPlugin(), "mob_nbt"); public static final NamespacedKey newMobTag = new NamespacedKey(SimpleBucketMobs.getPlugin(), "serialized_mob_data"); + @EventHandler(ignoreCancelled = true) public void onBucketMob(PlayerInteractEntityEvent interactEvent) { + // Only process main-hand interactions so we don't act on off-hand triggers + if (!interactEvent.getHand().equals(EquipmentSlot.HAND)) return; Entity entity = interactEvent.getRightClicked(); Player player = interactEvent.getPlayer(); if (!(entity instanceof LivingEntity livingEntity)) return; @@ -49,14 +52,18 @@ public void onBucketMob(PlayerInteractEntityEvent interactEvent) { if (!itemInHand.getType().equals(Material.BUCKET)) return; PersistentDataContainerView bucketPdcView = itemInHand.getPersistentDataContainer(); if (bucketPdcView.has(legacyMobTag) || bucketPdcView.has(newMobTag)) return; + // Cancel before modifying world state so other listeners see a clean cancellation + interactEvent.setCancelled(true); ItemStack bucketItem = BucketHandler.getMobBucket(livingEntity); BucketHandler.addMobBucketToInventory(player, bucketItem); - interactEvent.setCancelled(true); livingEntity.remove(); } @EventHandler(ignoreCancelled = true) public void onUnbucketMob(PlayerInteractEvent interactEvent) { + // Only process main-hand interactions; off-hand fires a duplicate event + EquipmentSlot hand = interactEvent.getHand(); + if (hand == null || !hand.equals(EquipmentSlot.HAND)) return; Player player = interactEvent.getPlayer(); ItemStack itemStack = player.getInventory().getItemInMainHand(); PersistentDataContainerView bucketPdc = itemStack.getPersistentDataContainer(); @@ -83,4 +90,4 @@ public void onBlockBucketEvent(PlayerBucketFillEvent bucketFillEvent){ if (itemUsed.getPersistentDataContainer().has(legacyMobTag) || itemUsed.getPersistentDataContainer().has(newMobTag)) bucketFillEvent.setCancelled(true); } -} +} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 9777f35..72dbf55 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -6,14 +6,14 @@ capture-item-style: default: name: default enchantment-glint: false - item-model: default + item-model: "minecraft:bucket" item-type: BUCKET stack-size: default captured-item-style: - use-resource-pack: true + use-resource-pack: false default: item-type: BUCKET - item-model: default + item-model: "minecraft:bucket" name: " in a Bucket" lore: - "A sits in the bucket" @@ -48,4 +48,4 @@ bucket-type-settings: allow: true MOOSHROOM: allow: true - sneak-required: true + sneak-required: true \ No newline at end of file From ec3359b1fe33d57ced78260bfb422f1f86827a3f Mon Sep 17 00:00:00 2001 From: Rhythmic Date: Sat, 18 Apr 2026 23:56:40 -0500 Subject: [PATCH 10/11] update readme --- README.md | 165 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 110 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 03caf6e..4d99121 100644 --- a/README.md +++ b/README.md @@ -2,89 +2,144 @@ [![wakatime](https://wakatime.com/badge/user/1f3b44b5-611a-484d-bcdc-b2084eefec1a/project/2a7588f5-f1d8-4dae-aa7b-fea0bf437f78.svg)](https://wakatime.com/@1f3b44b5-611a-484d-bcdc-b2084eefec1a/projects/szguvxhtkl) -Throw mobs into buckets! :D +Throw mobs into buckets - like axolotls, but for everything else. -## Permissions -| Permission | What it do | Default | -|---------------------------------|--------------------------------------------------|---------| -| `simplebucketmobs.bucket.` | Allows the user to bucket the specified mob | `false` | -| `simplebucketmobs.bucket.all` | Allows the user to bucket all enabled mobs | `op` | -| `simplebucketmobs.reload` | Reload plugin configuration files | `op` | -| `simplebucketmobs.debucket` | Dump saved mob NBT data from Mob Bucket to chat. | `op` | +Right-click a mob with an empty bucket to capture it. Right-click a block to release it. + +## Commands + +| Command | Description | Permission | +|---|---|---| +| `/sbm reload` | Reload all configuration files | `simplebucketmobs.reload` | +| `/sbm debucket` | Print NBT debug info for the bucket mob in your hand | `simplebucketmobs.debucket` | -## Texture Configuration +Aliases: `/simplebucketmobs`, `/simplebucketmob` + +## Permissions -This configuration is `texture.yml` which is specifically used for custom model data. Currently, the custom model data is applied to a bucket. +| Permission | Description | Default | +|---|---|---| +| `simplebucketmobs.bucket.` | Bucket a specific mob type (e.g. `simplebucketmobs.bucket.sheep`) | `false` | +| `simplebucketmobs.bucket.all` | Bucket all enabled mob types | `op` | +| `simplebucketmobs.reload` | Reload configuration files | `op` | +| `simplebucketmobs.debucket` | Dump saved mob data to chat | `op` | -The texture configuration is made to be customizable in the event you wanted to make special textures for each variant of a mob. +Per-mob permissions are only checked when `requires-permission: true` is set for that mob in `config.yml`. -For example, if you wanted to set a special blue sheep texture, rather than just having a generic texture for all sheep. +## Configuration (`config.yml`) -### Using "default" +### Captured Item Style -The simplest way to assign custom model data is to use the default option. +Controls the appearance of the bucket item produced when a mob is captured. ```yaml -SHEEP: - default: 1 -PIG: - default: 2 +captured-item-style: + use-resource-pack: true # Apply item-model from texture.yml (requires a resource pack) + default: + item-type: BUCKET # Material of the bucket item + item-model: "minecraft:bucket" # Fallback item model (NamespacedKey) + name: " in a Bucket" # MiniMessage display name + lore: + - "A sits in the bucket" + enchantment-glint: true # Whether the item has an enchantment glint ``` -This is the easiest way to set it and will set it to all mobs of that type. -In the above example, all sheep will have custom model data 1 and all pigs will have custom model data 2. +**Name placeholders:** -### Using the nbtKey and nbtValue +| Placeholder | Example output | +|---|---| +| `` | `SHEEP` | +| `` | `Sheep` | +| `` | Custom name if set, otherwise the mob's default display name | -nbtKey and nbtValue are the ways I will be referring to the NBT Tags. nbtValue is our "goal". The examples are simplified by removing the extra JSON. +### Mob Rules (`bucket-type-settings`) -Consider a villager's profession... +Controls which mobs can be bucketed and under what conditions. -```json -{ "VillagerData": {"level":1, "profession":"minecraft:none", "type":"minecraft:desert"} } +```yaml +bucket-type-settings: + default: + allow: false + sneak-required: false + pickup-when-aggro: false + requires-permission: false + types: + SHEEP: + allow: true + VILLAGER: + allow: true + sneak-required: true # Player must be sneaking to bucket this mob + requires-permission: true # Checks simplebucketmobs.bucket.villager + SLIME: + allow: true + pickup-when-aggro: true # Can be bucketed even when targeting the player ``` -If we want to differentiate custom model data based on a villager's profession, our nbtValue will be "minecraft:none" in this example. +Valid entity type names follow the [Bukkit EntityType enum](https://purpurmc.org/javadoc/org/bukkit/entity/EntityType.html). The following are always blocked regardless of config, since they are already bucketable in vanilla: `TROPICAL_FISH`, `SALMON`, `COD`, `AXOLOTL`, `PUFFERFISH`, `TADPOLE`. + +## Texture Configuration (`texture.yml`) + +`texture.yml` maps mob types (and their variants) to item model keys. These keys are [NamespacedKeys](https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/NamespacedKey.html) pointing to models in your resource pack. `use-resource-pack` in `config.yml` must be `true` for these to apply. + +The default values reference the [Bucket Mobs Resources](https://modrinth.com/resourcepack/bucket-mobs-resources/versions) resource pack. -You would get to the nbtValue using all the nbtKeys along the way. This will look like this in your `texture.yml`... +### Basic format + +Every mob entry supports a `default` key used as the fallback when no variant matches: ```yaml -VILLAGER: - VillagerData: - profession: - "minecraft:none": 1 +blaze: + default: "simplexity:bucket_mobs/misc/blaze" ``` -As shown above, you can use any information stored in the NBT tags to differentiate specific variants of a mob. Here are a few more examples... +### Variant format -**Parrot (Variant)** +For mobs whose variant is recognized by the plugin (see table below), sub-keys under `type` are matched against the variant name: -```json -{ "Variant": 1 } -``` ```yaml -PARROT: - default: 0 - Variant: - 1: 1 +frog: + default: "simplexity:bucket_mobs/frog/cold" + type: + cold: "simplexity:bucket_mobs/frog/cold" + temperate: "simplexity:bucket_mobs/frog/temperate" + warm: "simplexity:bucket_mobs/frog/warm" ``` -**Sheep (Color)** +### Supported variant lookups + +| Mob | `type` key format | Example keys | +|---|---|---| +| Cat | `Cat.Type` enum, lowercase | `tabby`, `black`, `red`, `siamese`, `british_shorthair`, `calico`, `persian`, `ragdoll`, `white`, `jellie`, `all_black` | +| Fox | `Fox.Type` enum, lowercase | `red`, `snow` | +| Frog | `Frog.Variant` enum, lowercase | `cold`, `temperate`, `warm` | +| Horse | `_