diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9230c1a..12842f5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,7 +35,7 @@ jobs: with: append_snapshot: ${{ github.ref_type == 'tag' && 'false' || 'true' }} version: ${{ github.ref_type == 'tag' && github.ref_name || '' }} - increment_version: ${{ github.ref_type == 'tag' && '' || 'patch' }} + increment_version: ${{ github.ref_type != 'tag' && 'patch' || '' }} increment_version_only_if_not_snapshot_version: ${{ github.ref == 'refs/heads/development' && 'true' || 'false' }} - name: Build with Maven diff --git a/UltimateStacker-API/pom.xml b/UltimateStacker-API/pom.xml index c1eea62..d2914f8 100644 --- a/UltimateStacker-API/pom.xml +++ b/UltimateStacker-API/pom.xml @@ -7,7 +7,7 @@ com.craftaro UltimateStacker-Parent - 3.0.1 + 3.1.2 UltimateStacker-API 1.0.0-SNAPSHOT diff --git a/UltimateStacker-Plugin/pom.xml b/UltimateStacker-Plugin/pom.xml index 451d78f..62980ac 100644 --- a/UltimateStacker-Plugin/pom.xml +++ b/UltimateStacker-Plugin/pom.xml @@ -7,7 +7,7 @@ com.craftaro UltimateStacker-Parent - 3.0.1 + 3.1.2 UltimateStacker-Plugin @@ -118,6 +118,13 @@ compile + + commons-lang + commons-lang + 2.6 + compile + + com.craftaro CraftaroCore @@ -128,7 +135,7 @@ org.spigotmc spigot-api - 1.18-R0.1-SNAPSHOT + 1.20-R0.1-SNAPSHOT provided diff --git a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/UltimateStacker.java b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/UltimateStacker.java index 1f1e41b..98f0869 100644 --- a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/UltimateStacker.java +++ b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/UltimateStacker.java @@ -10,12 +10,10 @@ import com.craftaro.core.dependency.Dependency; import com.craftaro.core.gui.GuiManager; import com.craftaro.core.hooks.EntityStackerManager; import com.craftaro.core.hooks.HologramManager; -import com.craftaro.core.hooks.HookManager; import com.craftaro.core.hooks.ProtectionManager; import com.craftaro.core.hooks.WorldGuardHook; -import com.craftaro.core.hooks.holograms.DecentHologramsHolograms; -import com.craftaro.third_party.com.cryptomorin.xseries.XMaterial; import com.craftaro.core.utils.TextUtils; +import com.craftaro.third_party.com.cryptomorin.xseries.XMaterial; import com.craftaro.ultimatestacker.api.UltimateStackerApi; import com.craftaro.ultimatestacker.api.stack.block.BlockStack; import com.craftaro.ultimatestacker.api.stack.block.BlockStackManager; @@ -25,13 +23,7 @@ import com.craftaro.ultimatestacker.api.stack.item.StackedItemManager; import com.craftaro.ultimatestacker.api.stack.spawner.SpawnerStack; import com.craftaro.ultimatestacker.api.stack.spawner.SpawnerStackManager; import com.craftaro.ultimatestacker.api.utils.Hologramable; -import com.craftaro.ultimatestacker.commands.CommandConvert; -import com.craftaro.ultimatestacker.commands.CommandGiveSpawner; -import com.craftaro.ultimatestacker.commands.CommandLootables; -import com.craftaro.ultimatestacker.commands.CommandReload; -import com.craftaro.ultimatestacker.commands.CommandRemoveAll; -import com.craftaro.ultimatestacker.commands.CommandSettings; -import com.craftaro.ultimatestacker.commands.CommandSpawn; +import com.craftaro.ultimatestacker.commands.*; import com.craftaro.ultimatestacker.database.migrations._1_InitialMigration; import com.craftaro.ultimatestacker.database.migrations._2_EntityStacks; import com.craftaro.ultimatestacker.database.migrations._3_BlockStacks; @@ -39,15 +31,7 @@ import com.craftaro.ultimatestacker.database.migrations._6_RemoveStackedEntityTa import com.craftaro.ultimatestacker.hook.StackerHook; import com.craftaro.ultimatestacker.hook.hooks.JobsHook; import com.craftaro.ultimatestacker.hook.hooks.SuperiorSkyblock2Hook; -import com.craftaro.ultimatestacker.listeners.BlockListeners; -import com.craftaro.ultimatestacker.listeners.BreedListeners; -import com.craftaro.ultimatestacker.listeners.ClearLagListeners; -import com.craftaro.ultimatestacker.listeners.DeathListeners; -import com.craftaro.ultimatestacker.listeners.InteractListeners; -import com.craftaro.ultimatestacker.listeners.ShearListeners; -import com.craftaro.ultimatestacker.listeners.SheepDyeListeners; -import com.craftaro.ultimatestacker.listeners.SpawnerListeners; -import com.craftaro.ultimatestacker.listeners.TameListeners; +import com.craftaro.ultimatestacker.listeners.*; import com.craftaro.ultimatestacker.listeners.entity.EntityCurrentListener; import com.craftaro.ultimatestacker.listeners.entity.EntityListeners; import com.craftaro.ultimatestacker.listeners.item.ItemCurrentListener; @@ -62,6 +46,7 @@ import com.craftaro.ultimatestacker.stackable.entity.custom.CustomEntityManager; import com.craftaro.ultimatestacker.stackable.item.StackedItemManagerImpl; import com.craftaro.ultimatestacker.stackable.spawner.SpawnerStackImpl; import com.craftaro.ultimatestacker.stackable.spawner.SpawnerStackManagerImpl; +import com.craftaro.ultimatestacker.tasks.BreedingTask; import com.craftaro.ultimatestacker.tasks.StackingTask; import com.craftaro.ultimatestacker.utils.Async; import org.apache.commons.lang.WordUtils; @@ -72,12 +57,7 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.PluginManager; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; +import java.util.*; public class UltimateStacker extends SongodaPlugin { @@ -99,8 +79,10 @@ public class UltimateStacker extends SongodaPlugin { private CommandManager commandManager; private CustomEntityManager customEntityManager; private StackingTask stackingTask; + private BreedingTask breedingTask; private UltimateStackerApi API; private SuperiorSkyblock2Hook superiorSkyblock2Hook; + private boolean instantStacking; public static UltimateStacker getInstance() { return INSTANCE; @@ -121,9 +103,8 @@ public class UltimateStacker extends SongodaPlugin { @Override public void onPluginDisable() { - if (this.stackingTask != null) { - this.stackingTask.stop(); - } + if (this.stackingTask != null) + this.stackingTask.cancel(); this.dataManager.saveBatchSync(this.spawnerStackManager.getStacksData()); this.dataManager.saveBatchSync(this.blockStackManager.getStacksData()); this.dataManager.shutdownNow(); @@ -153,7 +134,7 @@ public class UltimateStacker extends SongodaPlugin { new CommandGiveSpawner(this), new CommandSpawn(this), new CommandLootables(this), - new CommandConvert( guiManager) + new CommandConvert(guiManager) ); PluginManager pluginManager = Bukkit.getPluginManager(); @@ -212,6 +193,7 @@ public class UltimateStacker extends SongodaPlugin { if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)) pluginManager.registerEvents(new EntityCurrentListener(this), this); + this.instantStacking = Settings.STACK_ENTITIES.getBoolean() && Settings.INSTANT_STACKING.getBoolean(); pluginManager.registerEvents(new EntityListeners(this), this); pluginManager.registerEvents(new ItemListeners(this), this); @@ -246,7 +228,7 @@ public class UltimateStacker extends SongodaPlugin { public void onDataLoad() { if (HologramManager.isEnabled()) // Set the offset so that the holograms don't end up inside the blocks. - HologramManager.getHolograms().setPositionOffset(.5,.65,.5); + HologramManager.getHolograms().setPositionOffset(.5, .65, .5); // Load current data. final boolean useSpawnerHolo = Settings.SPAWNER_HOLOGRAMS.getBoolean(); @@ -262,16 +244,21 @@ public class UltimateStacker extends SongodaPlugin { } }); - this.stackingTask = new StackingTask(this); + //Start stacking task + if (Settings.STACK_ENTITIES.getBoolean()) { + this.breedingTask = new BreedingTask(this); + this.stackingTask = new StackingTask(this); + } + + this.instantStacking = Settings.STACK_ENTITIES.getBoolean() && Settings.INSTANT_STACKING.getBoolean(); final boolean useBlockHolo = Settings.BLOCK_HOLOGRAMS.getBoolean(); this.dataManager.loadBatch(BlockStackImpl.class, "blocks").forEach((data) -> { BlockStack blockStack = (BlockStack) data; this.blockStackManager.addBlock(blockStack); if (useBlockHolo) { if (blockStack == null) return; - if (blockStack.getLocation().getWorld() != null) { + if (blockStack.getLocation().getWorld() != null) updateHologram(blockStack); - } } }); } @@ -301,8 +288,12 @@ public class UltimateStacker extends SongodaPlugin { this.setLocale(getConfig().getString("System.Language Mode"), true); this.locale.reloadMessages(); - this.stackingTask.stop(); - this.stackingTask = new StackingTask(this); + if (stackingTask != null) + this.stackingTask.cancel(); + + if (Settings.STACK_ENTITIES.getBoolean()) { + this.stackingTask = new StackingTask(this); + } this.mobFile.load(); this.itemFile.load(); @@ -409,6 +400,10 @@ public class UltimateStacker extends SongodaPlugin { return superiorSkyblock2Hook; } + public boolean isInstantStacking() { + return instantStacking; + } + //////// Convenient API ////////// /** @@ -460,4 +455,8 @@ public class UltimateStacker extends SongodaPlugin { return !whitelist.isEmpty() && !whitelist.contains(combined) || !blacklist.isEmpty() && blacklist.contains(combined); } + + public BreedingTask getBreedingTask() { + return breedingTask; + } } diff --git a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/commands/CommandForceSpawn.java b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/commands/CommandForceSpawn.java new file mode 100644 index 0000000..01d56e8 --- /dev/null +++ b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/commands/CommandForceSpawn.java @@ -0,0 +1,69 @@ +package com.craftaro.ultimatestacker.commands; + +import com.craftaro.core.commands.AbstractCommand; +import com.craftaro.core.world.SSpawner; +import com.craftaro.third_party.com.cryptomorin.xseries.XMaterial; +import com.craftaro.ultimatestacker.UltimateStacker; +import com.craftaro.ultimatestacker.api.stack.spawner.SpawnerStack; +import org.bukkit.block.Block; +import org.bukkit.block.CreatureSpawner; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.Collections; +import java.util.List; + +public class CommandForceSpawn extends AbstractCommand { + private final UltimateStacker plugin; + + public CommandForceSpawn(UltimateStacker plugin) { + super(CommandType.PLAYER_ONLY, "forcespawn"); + this.plugin = plugin; + } + + @Override + protected ReturnType runCommand(CommandSender sender, String... args) { + Player player = (Player) sender; + + Block block = player.getTargetBlock(null, 200); + + if (XMaterial.matchXMaterial(block.getType().name()).get() != XMaterial.SPAWNER) { + this.plugin.getLocale().newMessage("&cThat is not a spawner...").sendPrefixedMessage(player); + return ReturnType.FAILURE; + } + + //PlacedSpawner spawner = this.plugin.getSpawnerManager().getSpawnerFromWorld(block.getLocation()); + SpawnerStack spawner = this.plugin.getSpawnerStackManager().getSpawner(block.getLocation()); + if (spawner == null) { + //it is a vanilla spawner + CreatureSpawner vanillaSpawner = (CreatureSpawner) block.getState(); + SSpawner creatureSpawner = new SSpawner(block.getLocation()); + creatureSpawner.spawn(vanillaSpawner.getSpawnCount(), vanillaSpawner.getSpawnedType()); + } else { + //it is an epic spawner + spawner.spawn(); + } + this.plugin.getLocale().newMessage("&aSpawning successful.").sendPrefixedMessage(player); + return ReturnType.SUCCESS; + } + + @Override + protected List onTab(CommandSender sender, String... args) { + return Collections.emptyList(); + } + + @Override + public String getPermissionNode() { + return "ultimatestacker.admin.forcespawn"; + } + + @Override + public String getSyntax() { + return "forcespawn"; + } + + @Override + public String getDescription() { + return "Force the spawner you are looking at to spawn so long as the spawn conditions are met."; + } +} diff --git a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/commands/CommandSpawn.java b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/commands/CommandSpawn.java index 9de3721..63d9a9f 100644 --- a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/commands/CommandSpawn.java +++ b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/commands/CommandSpawn.java @@ -4,6 +4,7 @@ import com.craftaro.core.commands.AbstractCommand; import com.craftaro.core.utils.TextUtils; import com.craftaro.ultimatestacker.UltimateStacker; import com.craftaro.ultimatestacker.api.stack.entity.EntityStack; +import com.craftaro.ultimatestacker.tasks.StackingTask; import org.bukkit.command.CommandSender; import org.bukkit.entity.EntityType; import org.bukkit.entity.LivingEntity; @@ -51,9 +52,12 @@ public class CommandSpawn extends AbstractCommand { } sender.sendMessage(TextUtils.formatText("&6" + list)); } else { - LivingEntity entity = (LivingEntity)player.getWorld().spawnEntity(player.getLocation(), type); - EntityStack stack = plugin.getEntityStackManager().createStackedEntity(entity, Integer.parseInt(args[1])); - plugin.getStackingTask().attemptSplit(stack, entity); + StackingTask stackingTask = plugin.getStackingTask(); + if (stackingTask != null) { + LivingEntity entity = (LivingEntity)player.getWorld().spawnEntity(player.getLocation(), type); + EntityStack stack = plugin.getEntityStackManager().createStackedEntity(entity, Integer.parseInt(args[1])); + stackingTask.attemptSplit(stack, -1); + } } return ReturnType.SUCCESS; diff --git a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/listeners/BreedListeners.java b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/listeners/BreedListeners.java index 5131dce..02d42f1 100644 --- a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/listeners/BreedListeners.java +++ b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/listeners/BreedListeners.java @@ -1,29 +1,39 @@ package com.craftaro.ultimatestacker.listeners; import com.craftaro.ultimatestacker.UltimateStacker; -import org.bukkit.Bukkit; +import com.craftaro.ultimatestacker.api.stack.entity.EntityStack; +import com.craftaro.ultimatestacker.api.stack.entity.EntityStackManager; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityBreedEvent; -import org.bukkit.metadata.FixedMetadataValue; public class BreedListeners implements Listener { private final UltimateStacker plugin; + private final EntityStackManager entityStackManager; public BreedListeners(UltimateStacker plugin) { this.plugin = plugin; + this.entityStackManager = plugin.getEntityStackManager(); } @EventHandler public void onBread(EntityBreedEvent event) { - Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> { - event.getFather().removeMetadata("breedCooldown", plugin); - event.getMother().removeMetadata("breedCooldown", plugin); - }, 5 * 20 * 60); - event.getFather().setMetadata("breedCooldown", new FixedMetadataValue(plugin, true)); - event.getFather().removeMetadata("inLove", plugin); - event.getMother().setMetadata("breedCooldown", new FixedMetadataValue(plugin, true)); - event.getMother().removeMetadata("inLove", plugin); + EntityStack stackedMother = entityStackManager.getStackedEntity(event.getMother()); + EntityStack stackedFather = entityStackManager.getStackedEntity(event.getFather()); + + plugin.getBreedingTask().addBreedingTicket(event.getMother(), event.getFather()); + + if (stackedMother != null) { + EntityStack stack = entityStackManager.getStackedEntity(event.getMother()); + if (stack.getAmount() <= 1) return; + stack.releaseHost(); + } + + if (stackedFather != null) { + EntityStack stack = entityStackManager.getStackedEntity(event.getFather()); + if (stack.getAmount() <= 1) return; + stack.releaseHost(); + } } } diff --git a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/listeners/DeathListeners.java b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/listeners/DeathListeners.java index b194cc1..e268743 100644 --- a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/listeners/DeathListeners.java +++ b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/listeners/DeathListeners.java @@ -77,12 +77,10 @@ public class DeathListeners implements Listener { List drops = custom ? plugin.getLootablesManager().getDrops(event.getEntity()) : event.getDrops().stream().map(Drop::new).collect(Collectors.toList()); - if (custom) { - for (ItemStack item : new ArrayList<>(event.getDrops())) { + if (custom) + for (ItemStack item : new ArrayList<>(event.getDrops())) if (shouldDrop(event.getEntity(), item.getType())) drops.add(new Drop(item)); - } - } if (plugin.getCustomEntityManager().getCustomEntity(entity) == null) { //Run commands here, or it will be buggy diff --git a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/listeners/InteractListeners.java b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/listeners/InteractListeners.java index a3cf2a8..8c93d83 100644 --- a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/listeners/InteractListeners.java +++ b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/listeners/InteractListeners.java @@ -1,20 +1,12 @@ package com.craftaro.ultimatestacker.listeners; -import com.craftaro.core.compatibility.ServerVersion; -import com.craftaro.third_party.com.cryptomorin.xseries.XMaterial; import com.craftaro.ultimatestacker.UltimateStacker; import com.craftaro.ultimatestacker.api.stack.entity.EntityStack; import com.craftaro.ultimatestacker.settings.Settings; import com.craftaro.ultimatestacker.stackable.entity.Split; import org.bukkit.Bukkit; import org.bukkit.Material; -import org.bukkit.entity.Ageable; -import org.bukkit.entity.Cat; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Horse; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Player; -import org.bukkit.entity.Wolf; +import org.bukkit.entity.*; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerInteractAtEntityEvent; @@ -64,66 +56,75 @@ public class InteractListeners implements Listener { && !((Ageable) entity).isAdult()) { return; } - entity.setMetadata("inLove", new FixedMetadataValue(plugin, true)); - Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, () -> { - if (entity.isDead()) return; - entity.removeMetadata("inLove", plugin); - }, 20 * 20); + plugin.getBreedingTask().addInLoveTicket(entity); } } private boolean correctFood(ItemStack is, Entity entity) { Material type = is.getType(); + switch (entity.getType().name()) { case "COW": case "MUSHROOM_COW": case "SHEEP": return type == Material.WHEAT; case "PIG": - return type == Material.CARROT || (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_9) && type == Material.BEETROOT) || type == Material.POTATO; + return type == Material.CARROT || type == Material.BEETROOT || type == Material.POTATO; case "CHICKEN": - return type == XMaterial.WHEAT_SEEDS.parseMaterial() + return type == Material.WHEAT_SEEDS || type == Material.MELON_SEEDS || type == Material.PUMPKIN_SEEDS - || type == XMaterial.BEETROOT_SEEDS.parseMaterial(); + || type == Material.BEETROOT_SEEDS; case "HORSE": - return (type == Material.GOLDEN_APPLE || type == Material.GOLDEN_CARROT) && ((Horse)entity).isTamed(); + case "DONKEY": + case "MULE": + return (type == Material.GOLDEN_APPLE || type == Material.GOLDEN_CARROT) && ((AbstractHorse) entity).isTamed(); case "WOLF": - return type == XMaterial.BEEF.parseMaterial() - || type == XMaterial.CHICKEN.parseMaterial() - || type == XMaterial.COD.parseMaterial() - || type == XMaterial.MUTTON.parseMaterial() - || type == XMaterial.PORKCHOP.parseMaterial() - || type == XMaterial.RABBIT.parseMaterial() - || XMaterial.SALMON.equals(XMaterial.matchXMaterial(is)) - || type == XMaterial.COOKED_BEEF.parseMaterial() - || type == XMaterial.COOKED_CHICKEN.parseMaterial() - || type == XMaterial.COOKED_COD.parseMaterial() - || type == XMaterial.COOKED_MUTTON.parseMaterial() - || type == XMaterial.COOKED_PORKCHOP.parseMaterial() - || type == XMaterial.COOKED_RABBIT.parseMaterial() - || XMaterial.COOKED_SALMON.equals(XMaterial.matchXMaterial(is)) + return type == Material.BEEF + || type == Material.CHICKEN + || type == Material.COD + || type == Material.MUTTON + || type == Material.PORKCHOP + || type == Material.RABBIT + || type == Material.SALMON + || type == Material.COOKED_BEEF + || type == Material.COOKED_CHICKEN + || type == Material.COOKED_COD + || type == Material.COOKED_MUTTON + || type == Material.COOKED_PORKCHOP + || type == Material.COOKED_RABBIT + || type == Material.COOKED_SALMON + || type == Material.ROTTEN_FLESH && ((Wolf) entity).isTamed(); case "OCELOT": - return (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13) - ? type == Material.SALMON - || type == Material.COD - || type == Material.PUFFERFISH - || type == Material.TROPICAL_FISH - : type == XMaterial.COD.parseMaterial()); // Now broken in 1.13 ((Ocelot) entity).isTamed() - case "PANDA": - return (type == Material.BAMBOO); - case "FOX": - return type == Material.SWEET_BERRIES; case "CAT": - return (type == Material.COD || type == Material.SALMON) && ((Cat) entity).isTamed(); + return (type == Material.COD || type == Material.SALMON) && ((Tameable) entity).isTamed(); + case "PANDA": + return type == Material.BAMBOO; + case "FOX": + return type == Material.SWEET_BERRIES || type == Material.GLOW_BERRIES; case "RABBIT": return type == Material.CARROT || type == Material.GOLDEN_CARROT || type == Material.DANDELION; case "LLAMA": + case "TRADER_LLAMA": return type == Material.HAY_BLOCK; case "TURTLE": return type == Material.SEAGRASS; + case "HOGLIN": + return type == Material.CRIMSON_FUNGUS; + case "STRIDER": + return type == Material.WARPED_FUNGUS; + case "BEE": + return type == Material.HONEYCOMB || type == Material.HONEY_BOTTLE; + case "AXOLOTL": + return type == Material.TROPICAL_FISH_BUCKET; + case "GOAT": + return type == Material.WHEAT; + case "GLOW_SQUID": + return type == Material.GLOW_INK_SAC; + case "CAMEL": + return type == Material.CACTUS; default: return false; } diff --git a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/listeners/entity/EntityListeners.java b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/listeners/entity/EntityListeners.java index 7357cd4..6bcd658 100644 --- a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/listeners/entity/EntityListeners.java +++ b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/listeners/entity/EntityListeners.java @@ -1,5 +1,6 @@ package com.craftaro.ultimatestacker.listeners.entity; +import com.craftaro.core.configuration.Config; import com.craftaro.third_party.com.cryptomorin.xseries.XMaterial; import com.craftaro.ultimatestacker.UltimateStacker; import com.craftaro.ultimatestacker.api.UltimateStackerApi; @@ -8,6 +9,7 @@ import com.craftaro.ultimatestacker.api.stack.entity.EntityStackManager; import com.craftaro.ultimatestacker.api.stack.item.StackedItem; import com.craftaro.ultimatestacker.api.stack.spawner.SpawnerStack; import com.craftaro.ultimatestacker.settings.Settings; +import com.craftaro.ultimatestacker.tasks.StackingTask; import com.craftaro.ultimatestacker.utils.Methods; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -38,9 +40,12 @@ import java.util.List; public class EntityListeners implements Listener { private final UltimateStacker plugin; + private int searchRadius = Settings.SEARCH_RADIUS.getInt() * 16; //SEARCH_RADIUS is in chunks, so multiply by 16 to get blocks + private Config mobsConfig; public EntityListeners(UltimateStacker plugin) { this.plugin = plugin; + mobsConfig = plugin.getMobFile(); } @EventHandler @@ -73,9 +78,7 @@ public class EntityListeners implements Listener { item.setAmount(Math.min(amount, item.getMaxStackSize())); if (amount > item.getMaxStackSize()) { StackedItem stackedItem = UltimateStackerApi.getStackedItemManager().getStackedItem(event.getEntity()); - if (stackedItem != null) { - stackedItem.setAmount(amount - item.getMaxStackSize()); - } + stackedItem.setAmount(amount - item.getMaxStackSize()); } event.getEntity().setItemStack(item); } @@ -102,7 +105,31 @@ public class EntityListeners implements Listener { @EventHandler public void onSpawn(CreatureSpawnEvent event) { - event.getEntity().setMetadata("US_REASON", new FixedMetadataValue(plugin, event.getSpawnReason().name())); + String spawnReason = event.getSpawnReason().name(); + if (plugin.isInstantStacking()) { + LivingEntity spawningEntity = event.getEntity(); + EntityStackManager stackManager = plugin.getEntityStackManager(); + if (stackManager.isStackedEntity(spawningEntity)) return; //We don't want to stack split entities or respawned stacks + + List stackableFriends = plugin.getStackingTask().getSimilarEntitiesAroundEntity(spawningEntity, spawningEntity.getLocation()); + if (stackableFriends.isEmpty()) { + event.getEntity().setMetadata("US_REASON", new FixedMetadataValue(plugin, spawnReason)); + return; + } + + LivingEntity friendStack = stackableFriends.get(0); + if (stackManager.isStackedEntity(friendStack)) { + EntityStack stack = stackManager.getStackedEntity(friendStack); + //getSimilarEntitiesAroundEntity check for max stack size, we don't need to check again + stack.add(1); + event.setCancelled(true); + } else { + stackManager.createStackedEntity(friendStack, 2); + } + return; + } + + event.getEntity().setMetadata("US_REASON", new FixedMetadataValue(plugin, spawnReason)); } @EventHandler diff --git a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/lootables/LootablesManager.java b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/lootables/LootablesManager.java index 096fd94..8ba1925 100644 --- a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/lootables/LootablesManager.java +++ b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/lootables/LootablesManager.java @@ -67,6 +67,7 @@ public class LootablesManager { //Apply SuperiorSkyblock2 mob-drops multiplier if present if (superiorSkyblock2Hook.isEnabled()) { for (Drop drop : toDrop) { + if (drop.getItemStack() == null) continue; //Maybe it is just exp drop.getItemStack().setAmount(superiorSkyblock2Hook.getDropMultiplier(entity.getLocation()) * drop.getItemStack().getAmount()); } } diff --git a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/settings/Settings.java b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/settings/Settings.java index 0956314..58cf8b5 100644 --- a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/settings/Settings.java +++ b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/settings/Settings.java @@ -19,6 +19,9 @@ public class Settings implements com.craftaro.ultimatestacker.api.Settings { "The speed in which a new stacks will be created.", "It is advised to keep this number low."); + public static final ConfigSetting INSTANT_STACKING = new ConfigSetting(config, "Main.Instant Stacking", false, + "Should entities stacked into existing stacks before they spawned?"); + public static final ConfigSetting DISABLED_WORLDS = new ConfigSetting(config, "Main.Disabled Worlds", Arrays.asList("World1", "World2", "World3"), "Worlds that stacking doesn't happen in."); @@ -45,8 +48,10 @@ public class Settings implements com.craftaro.ultimatestacker.api.Settings { "The maximum amount of each entity type stack allowed in a chunk."); public static final ConfigSetting STACK_WHOLE_CHUNK = new ConfigSetting(config, "Entities.Stack Whole Chunk", false, - "Should all qualifying entities in each chunk be stacked?", - "This will override the stacking radius."); + "Should all qualifying entities in each chunk be stacked?"); + + public static final ConfigSetting STACK_WHOLE_CHUNK_RADIUS = new ConfigSetting(config, "Entities.Stack Whole Chunk Radius", 1, + "Radius in chunks every direction around the entity that will be stacked.", "0 means only the chunk the entity is in."); public static final ConfigSetting ENTITY_NAMETAGS = new ConfigSetting(config, "Entities.Holograms Enabled", true, "Should holograms be displayed above stacked entities?"); @@ -103,7 +108,11 @@ public class Settings implements com.craftaro.ultimatestacker.api.Settings { "\"HORSE_STYLE\", \"HORSE_CARRYING_CHEST\", \"HORSE_HAS_ARMOR\", \"HORSE_HAS_SADDLE\",", "\"HORSE_JUMP\", \"RABBIT_TYPE\", \"VILLAGER_PROFESSION\", \"LLAMA_COLOR\",", "\"LLAMA_STRENGTH\", \"PARROT_TYPE\", \"PUFFERFISH_STATE\", \"TROPICALFISH_PATTERN\",", - "\"TROPICALFISH_BODY_COLOR\", \"TROPICALFISH_PATTERN_COLOR\", \"PHANTOM_SIZE\", \"CAT_TYPE\"."); + "\"TROPICALFISH_BODY_COLOR\", \"TROPICALFISH_PATTERN_COLOR\", \"PHANTOM_SIZE\", \"CAT_TYPE\"", + "\"AXOLOTL_VARIANT\", \"AXOLOTL_PLAYING_DEAD\", \"GLOW_SQUID_DARK_TICKS\", \"GOAT_HAS_HORNS\",", + "\"FROG_VARIANT\", \"TADPOLE_AGE\", \"WARDEN_ANGER_LEVEL\", \"SNIFFER_HAS_SEEDS\",", + "\"FOX_TYPE\", \"HOGLIN_IMMUNE\"."); + public static final ConfigSetting SPLIT_CHECKS = new ConfigSetting(config, "Entities.Split Checks", Arrays.asList(Split.values()).stream() .map(Split::name).collect(Collectors.toList()), diff --git a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/stackable/entity/Check.java b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/stackable/entity/Check.java index 90fee5b..065510f 100644 --- a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/stackable/entity/Check.java +++ b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/stackable/entity/Check.java @@ -19,7 +19,7 @@ public enum Check { SLIME_SIZE(true), PIG_SADDLE(true), SHEEP_SHEARED(true), - SHEEP_COLOR(false), + SHEEP_COLOR(true), SNOWMAN_DERPED(true), WOLF_COLLAR_COLOR(true), OCELOT_TYPE(false), @@ -39,8 +39,17 @@ public enum Check { TROPICALFISH_BODY_COLOR(true), TROPICALFISH_PATTERN_COLOR(true), PHANTOM_SIZE(true), - CAT_TYPE(false); - + CAT_TYPE(false), + AXOLOTL_VARIANT(false), + AXOLOTL_PLAYING_DEAD(true), + GLOW_SQUID_DARK_TICKS(true), + GOAT_HAS_HORNS(false), + FROG_VARIANT(true), + TADPOLE_AGE(false), + WARDEN_ANGER_LEVEL(false), + SNIFFER_HAS_SEEDS(true), + FOX_TYPE(false), + HOGLIN_IMMUNE(true); private final boolean isEnabledByDefault; private final static Map checks = new HashMap(); diff --git a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/stackable/entity/EntityStackImpl.java b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/stackable/entity/EntityStackImpl.java index eee5cfc..9e5bfc7 100644 --- a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/stackable/entity/EntityStackImpl.java +++ b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/stackable/entity/EntityStackImpl.java @@ -1,12 +1,10 @@ package com.craftaro.ultimatestacker.stackable.entity; -import com.craftaro.core.SongodaCore; import com.craftaro.core.compatibility.ServerVersion; import com.craftaro.core.lootables.loot.Drop; import com.craftaro.core.lootables.loot.DropUtils; import com.craftaro.core.utils.EntityUtils; import com.craftaro.ultimatestacker.UltimateStacker; -import com.craftaro.ultimatestacker.api.UltimateStackerApi; import com.craftaro.ultimatestacker.api.events.entity.EntityStackKillEvent; import com.craftaro.ultimatestacker.api.stack.entity.EntityStack; import com.craftaro.ultimatestacker.settings.Settings; @@ -14,7 +12,7 @@ import com.craftaro.ultimatestacker.utils.Async; import com.craftaro.ultimatestacker.utils.Methods; import org.bukkit.Bukkit; import org.bukkit.Location; -import org.bukkit.Material; +import org.bukkit.NamespacedKey; import org.bukkit.entity.EntityType; import org.bukkit.entity.ExperienceOrb; import org.bukkit.entity.LivingEntity; @@ -22,10 +20,10 @@ import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.metadata.FixedMetadataValue; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; import org.bukkit.util.Vector; -import java.math.BigDecimal; -import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.UUID; @@ -33,35 +31,73 @@ import java.util.UUID; public class EntityStackImpl implements EntityStack { private final UltimateStacker plugin = UltimateStacker.getInstance(); + private final NamespacedKey STACKED_ENTITY_KEY = new NamespacedKey(plugin, "US_AMOUNT"); private int amount; private LivingEntity hostEntity; /** * Gets an existing stack from an entity or creates a new one if it doesn't exist. + * * @param entity The entity to get the stack from. */ public EntityStackImpl(LivingEntity entity) { if (entity == null) return; if (!UltimateStacker.getInstance().getEntityStackManager().isStackedEntity(entity)) { - entity.setMetadata("US_AMOUNT", new FixedMetadataValue(UltimateStacker.getInstance(), 1)); - this.amount = 1; + if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_14)) { + PersistentDataContainer container = entity.getPersistentDataContainer(); + if (container.has(STACKED_ENTITY_KEY, PersistentDataType.INTEGER)) { + this.amount = container.get(STACKED_ENTITY_KEY, PersistentDataType.INTEGER); + entity.setMetadata("US_AMOUNT", new FixedMetadataValue(UltimateStacker.getInstance(), amount)); + } else { + entity.setMetadata("US_AMOUNT", new FixedMetadataValue(UltimateStacker.getInstance(), 1)); + this.amount = 1; + } + } else { + entity.setMetadata("US_AMOUNT", new FixedMetadataValue(UltimateStacker.getInstance(), 1)); + this.amount = 1; + } } else { - //get the amount from the entity - this.amount = entity.getMetadata("US_AMOUNT").get(0).asInt(); + if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_14)) { + PersistentDataContainer container = entity.getPersistentDataContainer(); + if (container.has(STACKED_ENTITY_KEY, PersistentDataType.INTEGER)) { + this.amount = container.get(STACKED_ENTITY_KEY, PersistentDataType.INTEGER); + } else { + this.amount = getMetaCount(entity); + } + } else { + this.amount = getMetaCount(entity); + } } this.hostEntity = entity; } + private int getMetaCount(LivingEntity entity) { + if (entity.hasMetadata("US_AMOUNT")) { + return entity.getMetadata("US_AMOUNT").get(0).asInt(); + } else { + return 1; + } + } + /** * Creates a new stack or overrides an existing stack. + * * @param entity The entity to create the stack for. * @param amount The amount of entities in the stack. */ public EntityStackImpl(LivingEntity entity, int amount) { if (entity == null) return; this.hostEntity = entity; + if (amount <= 0) { + throw new IllegalArgumentException("Amount must be greater than 0"); + } this.amount = amount; - entity.setMetadata("US_AMOUNT", new FixedMetadataValue(UltimateStacker.getInstance(), amount)); + if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_14)) { + PersistentDataContainer container = entity.getPersistentDataContainer(); + container.set(STACKED_ENTITY_KEY, PersistentDataType.INTEGER, amount); + } else { + entity.setMetadata("US_AMOUNT", new FixedMetadataValue(UltimateStacker.getInstance(), amount)); + } updateNameTag(); } @@ -78,7 +114,12 @@ public class EntityStackImpl implements EntityStack { @Override public void setAmount(int amount) { this.amount = amount; - this.hostEntity.setMetadata("US_AMOUNT", new FixedMetadataValue(UltimateStacker.getInstance(), amount)); + if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_14)) { + PersistentDataContainer container = hostEntity.getPersistentDataContainer(); + container.set(STACKED_ENTITY_KEY, PersistentDataType.INTEGER, amount); + } else { + hostEntity.setMetadata("US_AMOUNT", new FixedMetadataValue(UltimateStacker.getInstance(), amount)); + } updateNameTag(); } @@ -196,10 +237,10 @@ public class EntityStackImpl implements EntityStack { @Override public synchronized LivingEntity takeOneAndSpawnEntity(Location location) { if (amount <= 0) return null; + LivingEntity entity = (LivingEntity) Objects.requireNonNull(location.getWorld()).spawnEntity(location, hostEntity.getType()); - if (Settings.NO_AI.getBoolean()) { + if (Settings.NO_AI.getBoolean()) EntityUtils.setUnaware(entity); - } this.hostEntity = entity; setAmount(amount--); updateNameTag(); @@ -208,13 +249,27 @@ public class EntityStackImpl implements EntityStack { @Override public synchronized void releaseHost() { - LivingEntity oldHost = hostEntity; - LivingEntity entity = takeOneAndSpawnEntity(hostEntity.getLocation()); - if (getAmount() >= 0) { - plugin.getEntityStackManager().updateStack(oldHost, entity); - updateNameTag(); + wipeData(); + + //Summon a new entity, update the stack and remove the metadata from the old entity + this.hostEntity = takeOneAndSpawnEntity(hostEntity.getLocation()); + if (amount == 2) { + wipeData(); } else { - destroy(); + setAmount(amount - 1); + updateNameTag(); + } + } + + private synchronized void wipeData() { + hostEntity.setCustomName(null); + hostEntity.setCustomNameVisible(false); + + if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_14)) { + PersistentDataContainer container = hostEntity.getPersistentDataContainer(); + container.remove(STACKED_ENTITY_KEY); + } else { + hostEntity.removeMetadata("US_AMOUNT", plugin); } } @@ -226,9 +281,9 @@ public class EntityStackImpl implements EntityStack { } public void updateNameTag() { - if (hostEntity == null) { + if (hostEntity == null) return; - } + hostEntity.setCustomNameVisible(!Settings.HOLOGRAMS_ON_LOOK_ENTITY.getBoolean()); hostEntity.setCustomName(Methods.compileEntityName(hostEntity, getAmount())); } diff --git a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/stackable/entity/EntityStackManagerImpl.java b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/stackable/entity/EntityStackManagerImpl.java index bd4acf8..0db4cd6 100644 --- a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/stackable/entity/EntityStackManagerImpl.java +++ b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/stackable/entity/EntityStackManagerImpl.java @@ -1,13 +1,17 @@ package com.craftaro.ultimatestacker.stackable.entity; +import com.craftaro.core.compatibility.ServerVersion; import com.craftaro.ultimatestacker.UltimateStacker; import com.craftaro.ultimatestacker.api.stack.entity.EntityStack; import com.craftaro.ultimatestacker.api.stack.entity.EntityStackManager; import org.bukkit.Bukkit; +import org.bukkit.NamespacedKey; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.metadata.FixedMetadataValue; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; import uk.antiperson.stackmob.api.StackedEntity; import java.util.UUID; @@ -15,9 +19,11 @@ import java.util.UUID; public class EntityStackManagerImpl implements EntityStackManager { private final UltimateStacker plugin; + private final NamespacedKey STACKED_ENTITY_KEY; public EntityStackManagerImpl(UltimateStacker plugin) { this.plugin = plugin; + this.STACKED_ENTITY_KEY = new NamespacedKey(plugin, "US_AMOUNT"); } @Override @@ -29,7 +35,13 @@ public class EntityStackManagerImpl implements EntityStackManager { @Override public boolean isStackedEntity(Entity entity) { - return entity.hasMetadata("US_AMOUNT"); + if (entity.hasMetadata("US_AMOUNT")) { + return true; + } + if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_14)) { + return entity.getPersistentDataContainer().has(STACKED_ENTITY_KEY, PersistentDataType.INTEGER); + } + return false; } @Override diff --git a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/stackable/item/StackedItemManagerImpl.java b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/stackable/item/StackedItemManagerImpl.java index 36ff326..ae9365e 100644 --- a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/stackable/item/StackedItemManagerImpl.java +++ b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/stackable/item/StackedItemManagerImpl.java @@ -22,8 +22,6 @@ import java.util.concurrent.atomic.AtomicReference; public class StackedItemManagerImpl implements StackedItemManager { - private final static int MAX_INT = 1500000000; - @Override public @NotNull StackedItem getStackedItem(Item item) { return new StackedItemImpl(item); @@ -106,8 +104,8 @@ public class StackedItemManagerImpl implements StackedItemManager { } } - int maxItemStackSize = Settings.MAX_STACK_ITEMS.getInt(); - if (maxItemStackSize > MAX_INT) maxItemStackSize = MAX_INT; + long maxItemStackSize = Settings.MAX_STACK_ITEMS.getLong(); + if (maxItemStackSize > Integer.MAX_VALUE) maxItemStackSize = Integer.MAX_VALUE; ItemStack fromItemStack = from.getItemStack(); ItemStack toItemStack = to.getItemStack(); diff --git a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/stackable/spawner/SpawnerStackImpl.java b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/stackable/spawner/SpawnerStackImpl.java index d72e8b4..f427d13 100644 --- a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/stackable/spawner/SpawnerStackImpl.java +++ b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/stackable/spawner/SpawnerStackImpl.java @@ -84,7 +84,7 @@ public class SpawnerStackImpl implements SpawnerStack { @Override public int spawn() { - return spawn(-1, false); + return spawn(-1, Settings.NO_AI.getBoolean()); } @Override diff --git a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/tasks/BreedingTask.java b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/tasks/BreedingTask.java new file mode 100644 index 0000000..e30d6b8 --- /dev/null +++ b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/tasks/BreedingTask.java @@ -0,0 +1,48 @@ +package com.craftaro.ultimatestacker.tasks; + +import com.craftaro.core.task.TaskScheduler; +import com.craftaro.ultimatestacker.UltimateStacker; +import org.bukkit.entity.LivingEntity; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +public class BreedingTask extends TaskScheduler { + + private final UltimateStacker plugin; + + private final Set breedingEntities = new HashSet<>(); + private final Set inLoveEntities = new HashSet<>(); + + public BreedingTask(UltimateStacker plugin) { + super(plugin); + this.plugin = plugin; + + } + + public void addBreedingTicket(LivingEntity mother, LivingEntity father) { + UUID motherUUID = mother.getUniqueId(); + UUID fatherUUID = father.getUniqueId(); + + addTask(() -> { + breedingEntities.remove(motherUUID); + breedingEntities.remove(fatherUUID); + }, 20L * 1000L); + + breedingEntities.add(motherUUID); + inLoveEntities.remove(motherUUID); + breedingEntities.add(fatherUUID); + inLoveEntities.remove(fatherUUID); + } + + public void addInLoveTicket(LivingEntity entity) { + UUID entityUUID = entity.getUniqueId(); + addTask(() -> inLoveEntities.remove(entityUUID), 5L * 60L * 1000L); + inLoveEntities.add(entityUUID); + } + + public boolean isInQueue(UUID uniqueId) { + return breedingEntities.contains(uniqueId) || inLoveEntities.contains(uniqueId); + } +} diff --git a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/tasks/StackingTask.java b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/tasks/StackingTask.java index b90ce3f..242a69c 100644 --- a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/tasks/StackingTask.java +++ b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/tasks/StackingTask.java @@ -2,8 +2,8 @@ package com.craftaro.ultimatestacker.tasks; import com.craftaro.core.compatibility.ServerVersion; import com.craftaro.core.hooks.WorldGuardHook; -import com.craftaro.third_party.com.cryptomorin.xseries.XMaterial; import com.craftaro.core.world.SWorld; +import com.craftaro.third_party.com.cryptomorin.xseries.XMaterial; import com.craftaro.ultimatestacker.UltimateStacker; import com.craftaro.ultimatestacker.api.stack.entity.EntityStack; import com.craftaro.ultimatestacker.api.stack.entity.EntityStackManager; @@ -11,64 +11,24 @@ import com.craftaro.ultimatestacker.settings.Settings; import com.craftaro.ultimatestacker.stackable.entity.Check; import com.craftaro.ultimatestacker.stackable.entity.custom.CustomEntity; import com.craftaro.ultimatestacker.utils.CachedChunk; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; +import org.bukkit.*; import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.AbstractHorse; -import org.bukkit.entity.Ageable; -import org.bukkit.entity.ArmorStand; -import org.bukkit.entity.Cat; -import org.bukkit.entity.ChestedHorse; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Horse; -import org.bukkit.entity.HumanEntity; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Llama; -import org.bukkit.entity.Ocelot; -import org.bukkit.entity.Parrot; -import org.bukkit.entity.Phantom; -import org.bukkit.entity.Pig; -import org.bukkit.entity.PufferFish; -import org.bukkit.entity.Rabbit; -import org.bukkit.entity.Sheep; -import org.bukkit.entity.Skeleton; -import org.bukkit.entity.Slime; -import org.bukkit.entity.Snowman; -import org.bukkit.entity.Tameable; -import org.bukkit.entity.TropicalFish; -import org.bukkit.entity.Villager; -import org.bukkit.entity.Wolf; -import org.bukkit.entity.Zombie; +import org.bukkit.entity.*; import org.bukkit.inventory.EntityEquipment; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; +import org.bukkit.scheduler.BukkitRunnable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TimerTask; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; +import java.util.*; +import java.util.stream.Collectors; import static com.craftaro.ultimatestacker.stackable.entity.Check.getChecks; -public class StackingTask extends TimerTask { +public class StackingTask extends BukkitRunnable { private final UltimateStacker plugin; - private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); - private final EntityStackManager stackManager; + private final BreedingTask breedingTask; private final ConfigurationSection configurationSection = UltimateStacker.getInstance().getMobFile(); private final List processed = new ArrayList<>(); @@ -77,6 +37,7 @@ public class StackingTask extends TimerTask { private final int maxEntityStackSize = Settings.MAX_STACK_ENTITIES.getInt(), minEntityStackSize = Settings.MIN_STACK_ENTITIES.getInt(), searchRadius = Settings.SEARCH_RADIUS.getInt(), + chunkRadius = Settings.STACK_WHOLE_CHUNK_RADIUS.getInt(), maxPerTypeStacksPerChunk = Settings.MAX_PER_TYPE_STACKS_PER_CHUNK.getInt(); private final List disabledWorlds = Settings.DISABLED_WORLDS.getStringList(), stackReasons = Settings.STACK_REASONS.getStringList(); @@ -87,278 +48,259 @@ public class StackingTask extends TimerTask { onlyStackFromSpawners = Settings.ONLY_STACK_FROM_SPAWNERS.getBoolean(), onlyStackOnSurface = Settings.ONLY_STACK_ON_SURFACE.getBoolean(); - Set loadedWorlds = new HashSet<>(); + private final Set loadedWorlds; public StackingTask(UltimateStacker plugin) { this.plugin = plugin; - this.stackManager = plugin.getEntityStackManager(); + stackManager = plugin.getEntityStackManager(); + breedingTask = plugin.getBreedingTask(); + // Add loaded worlds. - for (World world : Bukkit.getWorlds()) + loadedWorlds = new HashSet<>(); + for (World world : Bukkit.getWorlds()) { + //Filter disabled worlds to avoid continuous checks in the stacking loop + if (isWorldDisabled(world)) continue; loadedWorlds.add(new SWorld(world)); + } - // Start the stacking task. - //runTaskTimerAsynchronously(plugin, 0, Settings.STACK_SEARCH_TICK_SPEED.getInt()); - executorService.scheduleAtFixedRate(this, 0, (Settings.STACK_SEARCH_TICK_SPEED.getInt()*50L), TimeUnit.MILLISECONDS); - } - - public void stop() { - executorService.shutdown(); + int tickRate = Settings.STACK_SEARCH_TICK_SPEED.getInt(); + runTaskTimer(plugin, tickRate, tickRate); } @Override public void run() { - //make sure if the task running if any error occurs + //Make sure to continue the task if any exception occurs try { - // Should entities be stacked? - if (!Settings.STACK_ENTITIES.getBoolean()) return; - // Loop through each world. for (SWorld sWorld : loadedWorlds) { - // If world is disabled then continue to the next world. - if (isWorldDisabled(sWorld.getWorld())) continue; - - // Get the loaded entities from the current world and reverse them. List entities; - try { - entities = getLivingEntitiesSync(sWorld).get(); - } catch (ExecutionException | InterruptedException ex) { - ex.printStackTrace(); - continue; + // Get the loaded entities from the current world and reverse them. + entities = sWorld.getLivingEntities(); + + //Filter non-stackable entities to improve performance on main thread + entities.removeIf(this::isEntityNotStackable); + + for (LivingEntity entity : entities) { + // Check our WorldGuard flag. + Boolean flag = WorldGuardHook.isEnabled() ? WorldGuardHook.getBooleanFlag(entity.getLocation(), "mob-stacking") : null; //Does this work async? + if (flag != null && !flag) + entities.removeIf(entity1 -> entity1.getUniqueId().equals(entity.getUniqueId())); } - Collections.reverse(entities); - Bukkit.getScheduler().runTask(plugin, () -> { - // Loop through the entities. - for (LivingEntity entity : entities) { - // Make sure our entity has not already been processed. - // Skip it if it has been. - if (this.processed.contains(entity.getUniqueId())) continue; + // Loop through the entities. + for (LivingEntity entity : entities) { + // Make sure our entity has not already been processed. + // Skip it if it has been. + if (processed.contains(entity.getUniqueId())) continue; - // Check to see if entity is not stackable. - if (!isEntityStackable(entity)) { - continue; - } + // Get entity location to pass around as its faster this way. + Location location = entity.getLocation(); - // Get entity location to pass around as its faster this way. - Location location = entity.getLocation(); - - // Process the entity. - this.processEntity(entity, sWorld, location); - } - }); + // Process the entity. + processEntity(entity, location, entities); + } } - // Clear caches in preparation for the next run. + } catch (Exception e) { + e.printStackTrace(); + } finally { + // Make sure we clear the processed list. this.processed.clear(); - } catch (Exception ignored) {} - } - private Future> getLivingEntitiesSync(SWorld sWorld) { - CompletableFuture> future = new CompletableFuture<>(); - Bukkit.getScheduler().scheduleSyncDelayedTask(this.plugin, () -> future.complete(sWorld.getLivingEntities())); - - return future; - } - - private Future getEntitiesInChunkSync(CachedChunk cachedChunk) { - CompletableFuture future = new CompletableFuture<>(); - Bukkit.getScheduler().runTask(this.plugin, () -> future.complete(cachedChunk.getEntities())); - return future; + } } public boolean isWorldDisabled(World world) { return disabledWorlds.stream().anyMatch(worldStr -> world.getName().equalsIgnoreCase(worldStr)); } - private boolean isEntityStackable(Entity entity) { + //Returns true if the entity is not stackable, and it will be removed from the list + private boolean isEntityNotStackable(LivingEntity entity) { + if (isMaxStack(entity)) return true; + // Make sure we have the correct entity type and that it is valid. if (!entity.isValid() || entity instanceof HumanEntity || entity instanceof ArmorStand - // Make sure the entity is not in love. - || entity.hasMetadata("inLove") - // Or in breeding cooldown. - || entity.hasMetadata("breedCooldown")) - return false; + // Make sure the entity is not in love or in the breeding queue. + || breedingTask.isInQueue(entity.getUniqueId())) + return true; - if (!configurationSection.getBoolean("Mobs." + entity.getType().name() + ".Enabled")) { - return false; - } + if (!configurationSection.getBoolean("Mobs." + entity.getType().name() + ".Enabled")) + return true; - // Allow spawn if stackreasons are set and match, or if from a spawner + // Allow spawn if stack reasons are set and match, or if from a spawner final String spawnReason = entity.hasMetadata("US_REASON") && !entity.getMetadata("US_REASON").isEmpty() ? entity.getMetadata("US_REASON").get(0).asString() : null; List stackReasons; if (onlyStackFromSpawners) { // If only stack from spawners is enabled, make sure the entity spawned from a spawner. if (!"SPAWNER".equals(spawnReason)) - return false; + return true; } else if (!(stackReasons = this.stackReasons).isEmpty() && !stackReasons.contains(spawnReason)) { // Only stack if on the list of events to stack - return false; + return true; } - // Cast our entity to living entity. - LivingEntity livingEntity = (LivingEntity) entity; - // If only stack on surface is enabled make sure the entity is on a surface then entity is stackable. - return !onlyStackOnSurface - || canFly(livingEntity) - || entity.getType().name().equals("SHULKER") - - || (livingEntity.isOnGround() - || (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13) - && livingEntity.isSwimming())); - + //return !onlyStackOnSurface || canFly(entity) || entity.getType().name().equals("SHULKER") || ((entity).isOnGround() || (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13) && (entity).isSwimming())); + return onlyStackOnSurface && canFly(entity) && !entity.getType().name().equals("SHULKER") && !entity.isOnGround() && !(ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13) && (entity).isSwimming()); } - private void processEntity(LivingEntity baseEntity, SWorld sWorld, Location location) { - - // Check our WorldGuard flag. - Boolean flag = WorldGuardHook.isEnabled() ? WorldGuardHook.getBooleanFlag(baseEntity.getLocation(), "mob-stacking") : null; - if (flag != null && !flag) { - return; - } - + private void processEntity(LivingEntity baseEntity, Location location, List entities) { // Get the stack from the entity. It should be noted that this value will // be null if our entity is not a stack. EntityStack baseStack = plugin.getEntityStackManager().getStackedEntity(baseEntity); // Get the maximum stack size for this entity. - int maxEntityStackSize = getEntityStackSize(baseEntity); + int maxEntityStackSize = getEntityMaxStackSize(baseEntity); // Is this entity stacked? boolean isStack = baseStack != null; - if (isStack && baseStack.getAmount() == maxEntityStackSize) { - // If the stack is already at the max size then we can skip it. - processed.add(baseEntity.getUniqueId()); + // The amount that is stackable. + int baseSize = isStack ? baseStack.getAmount() : 1; + + // Attempt to split overstacked entities. + // If this is successful, we can return because the entity was processed + if (isStack && attemptSplit(baseStack, maxEntityStackSize)) { return; } - // The amount that is stackable. - int amountToStack = isStack ? baseStack.getAmount() : 1; - - // Attempt to split our stack. If the split is successful then skip this entity. - if (isStack && attemptSplit(baseStack, baseEntity)) return; - - // If this entity is named, a custom entity or disabled then skip it. - if (!isStack && (baseEntity.getCustomName() != null - && plugin.getCustomEntityManager().getCustomEntity(baseEntity) == null) - || !configurationSection.getBoolean("Mobs." + baseEntity.getType().name() + ".Enabled")) { + // If this entity is named or a custom entity skip it. + if (!isStack && (baseEntity.getCustomName() != null && plugin.getCustomEntityManager().getCustomEntity(baseEntity) != null)) { processed.add(baseEntity.getUniqueId()); return; } // Get similar entities around our entity and make sure those entities are both compatible and stackable. - List stackableFriends = new LinkedList<>(); - List list = getSimilarEntitiesAroundEntity(baseEntity, sWorld, location); - for (LivingEntity entity : list) { - // Check to see if entity is not stackable. - if (!isEntityStackable(entity)) - continue; - // Add this entity to our stackable friends. - stackableFriends.add(entity); - } + List stackableFriends = getSimilarEntitiesAroundEntity(baseEntity, location); + + //Total entities that can be stacked into the base entity + int maxStackable = maxEntityStackSize - baseSize; + int toStack = 0; + List remove = new ArrayList<>(); // Loop through our similar stackable entities. for (LivingEntity friendlyEntity : stackableFriends) { - // Make sure the friendlyEntity has not already been processed. - if (this.processed.contains(friendlyEntity.getUniqueId())) continue; - // Get this entities friendStack. + if (!entities.contains(friendlyEntity)) + continue; + + // Process similar entities. EntityStack friendStack = stackManager.getStackedEntity(friendlyEntity); int amount = friendStack != null ? friendStack.getAmount() : 1; - - // Check to see if this friendlyEntity is stacked and friendStack plus - // our amount to stack is not above our max friendStack size - // for this friendlyEntity. - - boolean overstack = (amount + amountToStack) > maxEntityStackSize; - - if (!overstack) { - stackManager.createStackedEntity(friendlyEntity, amount + amountToStack); - processed.add(baseEntity.getUniqueId()); - - Bukkit.getScheduler().runTask(plugin, () -> { - if (baseEntity.isLeashed()) { - baseEntity.getWorld().dropItemNaturally(baseEntity.getLocation(), XMaterial.LEAD.parseItem()); - } - baseEntity.remove(); - }); - return; + if (toStack + amount <= maxStackable) { + toStack += amount; + remove.add(friendlyEntity); + continue; } + break; //We max, exit loop + } + + //Nothing to stack + if (toStack == 0) { + return; + } + + //Add to base stack and remove stacked friends + stackManager.createStackedEntity(baseEntity, baseSize + toStack); + processed.add(baseEntity.getUniqueId()); + + //Remove merged entities + //We in sync, so we can remove entities + for (LivingEntity entity : remove) { + processed.add(entity.getUniqueId()); + entity.remove(); } } - public boolean attemptSplit(EntityStack baseStack, LivingEntity livingEntity) { + /** + * This method splitting overstacked entities into new stacks. + * Must be called synchronously. + * + * @param baseStack The base stack to check for splitting. + * @param maxEntityStackSize The maximum stack size for the entity. -1 if we need to calculate it. + * @return True if the split was successful, false otherwise. + */ + public boolean attemptSplit(EntityStack baseStack, int maxEntityStackSize) { + LivingEntity hostEntity = baseStack.getHostEntity(); int stackSize = baseStack.getAmount(); - int maxEntityStackAmount = getEntityStackSize(livingEntity); + int maxEntityStackAmount = maxEntityStackSize == -1 ? getEntityMaxStackSize(hostEntity) : maxEntityStackSize; if (stackSize <= maxEntityStackAmount) return false; baseStack.setAmount(maxEntityStackAmount); - Bukkit.getScheduler().runTask(plugin, () -> { - int finalStackSize = stackSize - maxEntityStackAmount; - do { - // Create a new stack, summon entity and add to stack. - LivingEntity newEntity = (LivingEntity) livingEntity.getWorld().spawnEntity(livingEntity.getLocation(), livingEntity.getType()); - int toAdd = Math.min(finalStackSize, maxEntityStackAmount); - EntityStack newStack = stackManager.createStackedEntity(newEntity, toAdd); - processed.add(newEntity.getUniqueId()); - finalStackSize -= maxEntityStackAmount; - } while (finalStackSize >= 0); - }); + int finalStackSize = stackSize - maxEntityStackAmount; + do { + // Create a new stack, summon entity and add to stack. + LivingEntity newEntity = (LivingEntity) hostEntity.getWorld().spawnEntity(hostEntity.getLocation(), hostEntity.getType()); + int toAdd = Math.min(finalStackSize, maxEntityStackAmount); + EntityStack newStack = stackManager.createStackedEntity(newEntity, toAdd); + processed.add(newEntity.getUniqueId()); + finalStackSize -= maxEntityStackAmount; + } while (finalStackSize >= 0); //Mark it as processed. - processed.add(livingEntity.getUniqueId()); + processed.add(hostEntity.getUniqueId()); return true; } - private Set getNearbyChunks(SWorld sWorld, Location location, double radius, boolean singleChunk) { - //get current chunk - if (radius == -1) { - return new HashSet<>(Collections.singletonList(new CachedChunk(sWorld, location.getChunk().getX(), location.getChunk().getZ()))); + private Set getNearbyChunks(SWorld sWorld, Location location) { + //Only stack entities in the same chunk + if (stackWholeChunk && chunkRadius == 0) { + return Collections.singleton(new CachedChunk(sWorld, location.getChunk().getX(), location.getChunk().getZ())); } World world = location.getWorld(); - Set chunks = new HashSet<>(); - if (world == null) return chunks; + if (world == null) return new HashSet<>(); CachedChunk firstChunk = new CachedChunk(sWorld, location); + Set chunks = new TreeSet<>(Comparator.comparingInt(CachedChunk::getX).thenComparingInt(CachedChunk::getZ)); chunks.add(firstChunk); - if (singleChunk) return chunks; - - int minX = (int) Math.floor(((location.getX() - radius) - 2.0D) / 16.0D); - int maxX = (int) Math.floor(((location.getX() + radius) + 2.0D) / 16.0D); - int minZ = (int) Math.floor(((location.getZ() - radius) - 2.0D) / 16.0D); - int maxZ = (int) Math.floor(((location.getZ() + radius) + 2.0D) / 16.0D); + //Calculate chunk coordinates we need to check + int minX = (int) Math.floor((location.getX() - chunkRadius) / 16.0D); + int maxX = (int) Math.floor((location.getX() + chunkRadius) / 16.0D); + int minZ = (int) Math.floor((location.getZ() - chunkRadius) / 16.0D); + int maxZ = (int) Math.floor((location.getZ() + chunkRadius) / 16.0D); for (int x = minX; x <= maxX; ++x) { for (int z = minZ; z <= maxZ; ++z) { - if (firstChunk.getX() == x && firstChunk.getZ() == z) continue; - chunks.add(new CachedChunk(sWorld, x, z)); + if (x == minX || x == maxX || z == minZ || z == maxZ) { + chunks.add(new CachedChunk(sWorld, x, z)); + } } } + + //Set a bedrock in the top left corner of the chunks + for (CachedChunk chunk : chunks) { + int x = chunk.getX() * 16; + int z = chunk.getZ() * 16; + world.getBlockAt(x, 319, z).setType(XMaterial.BEDROCK.parseMaterial()); + } return chunks; } /** * Get all entities around an entity within a radius which are similar to the entity. + * * @param entity The entity to get similar entities around. - * @param radius The radius to get entities around. - * @param singleChunk Whether to only get entities in the same chunk as the entity. * @return A list of similar entities around the entity. */ - private List getFriendlyStacksNearby(LivingEntity entity, double radius, boolean singleChunk) { + public List getFriendlyStacksNearby(LivingEntity entity) { + if (!stackWholeChunk) { + return entity.getNearbyEntities(searchRadius / 2.0, searchRadius / 2.0, searchRadius / 2.0) + .stream().filter(e -> e.getType() == entity.getType() && !isMaxStack((LivingEntity) e)) + .map(e -> (LivingEntity) e).collect(Collectors.toList()); + } List entities = new ArrayList<>(); try { - Set chunks = getNearbyChunks(new SWorld(entity.getWorld()), entity.getLocation(), radius, singleChunk); + Set chunks = getNearbyChunks(new SWorld(entity.getWorld()), entity.getLocation()); for (CachedChunk chunk : chunks) { Entity[] entityList = chunk.getEntities(); for (Entity e : entityList) { - if (!processed.contains(e.getUniqueId()) && e.getType() == entity.getType() && e instanceof LivingEntity && e.isValid() && e.getLocation().distance(entity.getLocation()) <= radius) { + if (e.getType() == entity.getType() && !isMaxStack((LivingEntity) e)) { entities.add((LivingEntity) e); } } @@ -370,10 +312,10 @@ public class StackingTask extends TimerTask { return entities; } - public List getSimilarEntitiesAroundEntity(LivingEntity initialEntity, SWorld sWorld, Location location) { + public List getSimilarEntitiesAroundEntity(LivingEntity initialEntity, Location location) { try { // Create a list of all entities around the initial entity of the same type. - List entityList = new LinkedList<>(getFriendlyStacksNearby(initialEntity, searchRadius, stackWholeChunk)); + List entityList = new ArrayList<>(getFriendlyStacksNearby(initialEntity)); CustomEntity customEntity = plugin.getCustomEntityManager().getCustomEntity(initialEntity); if (customEntity != null) @@ -616,6 +558,64 @@ public class StackingTask extends TimerTask { entityList.removeIf(entity -> ((Phantom) entity).getSize() != phantom.getSize()); break; } + case AXOLOTL_VARIANT: { + if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_17) + || !(initialEntity instanceof Axolotl)) break; + Axolotl axolotl = (Axolotl) initialEntity; + entityList.removeIf(entity -> ((Axolotl) entity).getVariant() != axolotl.getVariant()); + break; + } + case GOAT_HAS_HORNS: { + if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_17) + || !(initialEntity instanceof Goat)) break; + Goat goat = (Goat) initialEntity; + boolean hasLeftHorn = goat.hasLeftHorn(); + boolean hasRightHorn = goat.hasRightHorn(); + entityList.removeIf(entity -> { + Goat otherGoat = (Goat) entity; + return otherGoat.hasLeftHorn() != hasLeftHorn || otherGoat.hasRightHorn() != hasRightHorn; + }); + break; + } + case FROG_VARIANT: { + if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_19) + || !(initialEntity instanceof Frog)) break; + Frog frog = (Frog) initialEntity; + entityList.removeIf(entity -> ((Frog) entity).getVariant() != frog.getVariant()); + break; + } + case TADPOLE_AGE: { + if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_19) + || !(initialEntity instanceof Tadpole)) break; + Tadpole tadpole = (Tadpole) initialEntity; + entityList.removeIf(entity -> ((Tadpole) entity).getAge() != tadpole.getAge()); + break; + } + case WARDEN_ANGER_LEVEL: { + if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_19) + || !(initialEntity instanceof Warden)) break; + Warden warden = (Warden) initialEntity; + entityList.removeIf(entity -> ((Warden) entity).getAnger() != warden.getAnger()); + break; + } + case FOX_TYPE: { + if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_14) + || !(initialEntity instanceof Fox)) break; + Fox fox = (Fox) initialEntity; + entityList.removeIf(entity -> ((Fox) entity).getFoxType() != fox.getFoxType()); + break; + } + case HOGLIN_IMMUNE: { + if (!ServerVersion.isServerVersionAtLeast(ServerVersion.V1_16) + || !(initialEntity instanceof Hoglin)) break; + Hoglin hoglin = (Hoglin) initialEntity; + if (hoglin.isImmuneToZombification()) { + entityList.removeIf(entity -> !((Hoglin) entity).isImmuneToZombification()); + } else { + entityList.removeIf(entity -> ((Hoglin) entity).isImmuneToZombification()); + } + break; + } } } @@ -641,16 +641,20 @@ public class StackingTask extends TimerTask { || (equipment.getBoots() != null && equipment.getBoots().getType() != Material.AIR)); } - private int getEntityStackSize(LivingEntity initialEntity) { - Integer max = entityStackSizes.get(initialEntity.getType()); - if (max == null) { - max = configurationSection.getInt("Mobs." + initialEntity.getType().name() + ".Max Stack Size"); - if (max == -1) { - max = maxEntityStackSize; + private int getEntityMaxStackSize(LivingEntity initialEntity) { + return entityStackSizes.computeIfAbsent(initialEntity.getType(), type -> { + int maxStackSize = configurationSection.getInt("Mobs." + initialEntity.getType().name() + ".Max Stack Size"); + if (maxStackSize == -1) { + maxStackSize = maxEntityStackSize; } - entityStackSizes.put(initialEntity.getType(), max); - } - return max; + return maxStackSize; + }); + } + + private boolean isMaxStack(LivingEntity livingEntity) { + EntityStack stack = stackManager.getStackedEntity(livingEntity); + if (stack == null) return false; + return stack.getAmount() >= getEntityMaxStackSize(livingEntity); } public boolean canFly(LivingEntity entity) { diff --git a/pom.xml b/pom.xml index 94e9f63..e03a41d 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.craftaro UltimateStacker-Parent pom - 3.0.1 + 3.1.2 UltimateStacker-API @@ -19,7 +19,7 @@ https://craftaro.com/marketplace/product/16 - 3.0.0-SNAPSHOT + 3.0.1-SNAPSHOT 8 1.8