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