From 7120f9895a2937179225b45f250042eb86e91a3d Mon Sep 17 00:00:00 2001 From: ceze88 Date: Fri, 2 Feb 2024 15:58:40 +0100 Subject: [PATCH 01/12] Improve stacking task speed. Add some new settings and instant stacking --- .../UltimateStacker.java | 20 +- .../commands/CommandForceSpawn.java | 69 +++++ .../commands/CommandSpawn.java | 10 +- .../listeners/BreedListeners.java | 19 ++ .../listeners/entity/EntityListeners.java | 35 ++- .../lootables/LootablesManager.java | 1 + .../settings/Settings.java | 9 +- .../stackable/entity/EntityStackImpl.java | 56 +++- .../entity/EntityStackManagerImpl.java | 14 +- .../stackable/spawner/SpawnerStackImpl.java | 2 +- .../tasks/StackingTask.java | 292 +++++++++--------- 11 files changed, 367 insertions(+), 160 deletions(-) create mode 100644 UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/commands/CommandForceSpawn.java 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..b2b5a65 100644 --- a/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/UltimateStacker.java +++ b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/UltimateStacker.java @@ -101,6 +101,7 @@ public class UltimateStacker extends SongodaPlugin { private StackingTask stackingTask; private UltimateStackerApi API; private SuperiorSkyblock2Hook superiorSkyblock2Hook; + private boolean instantStacking; public static UltimateStacker getInstance() { return INSTANCE; @@ -212,6 +213,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); @@ -262,7 +264,11 @@ public class UltimateStacker extends SongodaPlugin { } }); - this.stackingTask = new StackingTask(this); + if (Settings.STACK_ENTITIES.getBoolean()) { + //Start stacking task + 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; @@ -301,8 +307,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.stop(); + } + if (Settings.STACK_ENTITIES.getBoolean()) { + this.stackingTask = new StackingTask(this); + } this.mobFile.load(); this.itemFile.load(); @@ -409,6 +419,10 @@ public class UltimateStacker extends SongodaPlugin { return superiorSkyblock2Hook; } + public boolean isInstantStacking() { + return instantStacking; + } + //////// Convenient API ////////// /** 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..700c24d 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,6 +1,7 @@ package com.craftaro.ultimatestacker.listeners; import com.craftaro.ultimatestacker.UltimateStacker; +import com.craftaro.ultimatestacker.api.stack.entity.EntityStack; import org.bukkit.Bukkit; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -17,6 +18,24 @@ public class BreedListeners implements Listener { @EventHandler public void onBread(EntityBreedEvent event) { + //TODO: Fix breed. It removes a entity from the stack but not spawn a new one. (Splitting mechanic) + boolean isMotherStacked = plugin.getEntityStackManager().isStackedEntity(event.getMother()); + boolean isFatherStacked = plugin.getEntityStackManager().isStackedEntity(event.getFather()); + + if (!isMotherStacked && !isFatherStacked) return; + + if (isMotherStacked) { + EntityStack stack = plugin.getEntityStackManager().getStackedEntity(event.getMother()); + if (stack.getAmount() <= 1) return; + stack.releaseHost(); + } + + if (isFatherStacked) { + EntityStack stack = plugin.getEntityStackManager().getStackedEntity(event.getFather()); + if (stack.getAmount() <= 1) return; + stack.releaseHost(); + } + Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> { event.getFather().removeMetadata("breedCooldown", plugin); event.getMother().removeMetadata("breedCooldown", plugin); 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..df39767 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?"); 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..f1d7ef5 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 @@ -15,6 +15,7 @@ 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,6 +23,8 @@ 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; @@ -33,6 +36,7 @@ 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; @@ -43,15 +47,42 @@ public class EntityStackImpl implements EntityStack { 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. @@ -60,8 +91,16 @@ public class EntityStackImpl implements EntityStack { 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 +117,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(); } 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/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/StackingTask.java b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/tasks/StackingTask.java index b90ce3f..34d2f8e 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 @@ -46,6 +46,7 @@ import org.bukkit.inventory.EntityEquipment; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -53,6 +54,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.TimerTask; +import java.util.TreeSet; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -60,13 +62,14 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import static com.craftaro.ultimatestacker.stackable.entity.Check.getChecks; public class StackingTask extends TimerTask { private final UltimateStacker plugin; - private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); + private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(r -> new Thread(r, "UltimateStacker-Stacking-Thread")); private final EntityStackManager stackManager; @@ -77,6 +80,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,17 +91,19 @@ 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(); // 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); } @@ -107,25 +113,30 @@ public class StackingTask extends TimerTask { @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; + // Get the loaded entities from the current world and reverse them. try { entities = getLivingEntitiesSync(sWorld).get(); } catch (ExecutionException | InterruptedException ex) { ex.printStackTrace(); continue; } - Collections.reverse(entities); + + //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())); + } + } Bukkit.getScheduler().runTask(plugin, () -> { // Loop through the entities. @@ -134,28 +145,26 @@ public class StackingTask extends TimerTask { // Skip it if it has been. if (this.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(); // Process the entity. - this.processEntity(entity, sWorld, location); + this.processEntity(entity, location); } }); } - // Clear caches in preparation for the next run. + } catch (Exception ignored) { + + } 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; } @@ -169,7 +178,11 @@ public class StackingTask extends TimerTask { 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 @@ -178,187 +191,182 @@ public class StackingTask extends TimerTask { // Make sure the entity is not in love. || entity.hasMetadata("inLove") // Or in breeding cooldown. - || entity.hasMetadata("breedCooldown")) - return false; - - if (!configurationSection.getBoolean("Mobs." + entity.getType().name() + ".Enabled")) { - return false; + || entity.hasMetadata("breedCooldown")) { + return true; } - // Allow spawn if stackreasons are set and match, or if from a spawner + if (!configurationSection.getBoolean("Mobs." + entity.getType().name() + ".Enabled")) { + return true; + } + + // 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; + if (!"SPAWNER".equals(spawnReason)) { + 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) { // 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. + // 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 +378,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) @@ -641,16 +649,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) { From 21614461f9b6fdb0cb1e7bb06fe93552a3aefb3d Mon Sep 17 00:00:00 2001 From: ceze88 Date: Fri, 2 Feb 2024 15:58:50 +0100 Subject: [PATCH 02/12] Release v3.1.0 --- UltimateStacker-API/pom.xml | 2 +- UltimateStacker-Plugin/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/UltimateStacker-API/pom.xml b/UltimateStacker-API/pom.xml index c1eea62..70355c2 100644 --- a/UltimateStacker-API/pom.xml +++ b/UltimateStacker-API/pom.xml @@ -7,7 +7,7 @@ com.craftaro UltimateStacker-Parent - 3.0.1 + 3.1.0 UltimateStacker-API 1.0.0-SNAPSHOT diff --git a/UltimateStacker-Plugin/pom.xml b/UltimateStacker-Plugin/pom.xml index 451d78f..b69a9c7 100644 --- a/UltimateStacker-Plugin/pom.xml +++ b/UltimateStacker-Plugin/pom.xml @@ -7,7 +7,7 @@ com.craftaro UltimateStacker-Parent - 3.0.1 + 3.1.0 UltimateStacker-Plugin diff --git a/pom.xml b/pom.xml index 94e9f63..1b0acf0 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.craftaro UltimateStacker-Parent pom - 3.0.1 + 3.1.0 UltimateStacker-API From 4454296eebcfee9a017157b93ae6f7b04a851849 Mon Sep 17 00:00:00 2001 From: ceze88 Date: Wed, 7 Feb 2024 16:03:11 +0100 Subject: [PATCH 03/12] Fix nametag splitting --- .../stackable/entity/EntityStackImpl.java | 32 ++++++++++++++++--- .../tasks/StackingTask.java | 26 +++++++++++++++ 2 files changed, 53 insertions(+), 5 deletions(-) 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 f1d7ef5..f54c84f 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 @@ -252,14 +252,35 @@ public class EntityStackImpl implements EntityStack { @Override public synchronized void releaseHost() { + //Remove the metadata from the entity if it's the last one +// if (getAmount() == 1) { +// if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_14)) { +// PersistentDataContainer container = hostEntity.getPersistentDataContainer(); +// container.remove(STACKED_ENTITY_KEY); +// } else { +// hostEntity.removeMetadata("US_AMOUNT", plugin); +// } +// hostEntity.setCustomName(null); +// hostEntity.setCustomNameVisible(false); +// return; +// } + LivingEntity oldHost = hostEntity; - LivingEntity entity = takeOneAndSpawnEntity(hostEntity.getLocation()); - if (getAmount() >= 0) { - plugin.getEntityStackManager().updateStack(oldHost, entity); - updateNameTag(); + if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_14)) { + PersistentDataContainer container = oldHost.getPersistentDataContainer(); + container.remove(STACKED_ENTITY_KEY); + //Add new entry that the entity was split by player, not to stack it anymore + container.set(new NamespacedKey(plugin, "US_SPLIT_PLAYER"), PersistentDataType.BYTE, (byte) 1); } else { - destroy(); + oldHost.removeMetadata("US_AMOUNT", plugin); + //Add new entry that the entity was split by player, not to stack it anymore + oldHost.setMetadata("US_SPLIT_PLAYER", new FixedMetadataValue(plugin, true)); } + + //Summon a new entity and update the stack and remove the metadata from the old entity + this.hostEntity = takeOneAndSpawnEntity(hostEntity.getLocation()); + setAmount(amount-1); + updateNameTag(); } @Override @@ -273,6 +294,7 @@ public class EntityStackImpl implements EntityStack { 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/tasks/StackingTask.java b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/tasks/StackingTask.java index 34d2f8e..717d17a 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 @@ -14,6 +14,7 @@ import com.craftaro.ultimatestacker.utils.CachedChunk; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.NamespacedKey; import org.bukkit.World; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.AbstractHorse; @@ -43,6 +44,8 @@ import org.bukkit.entity.Villager; import org.bukkit.entity.Wolf; import org.bukkit.entity.Zombie; import org.bukkit.inventory.EntityEquipment; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; import java.util.ArrayList; import java.util.Collections; @@ -92,9 +95,11 @@ public class StackingTask extends TimerTask { onlyStackOnSurface = Settings.ONLY_STACK_ON_SURFACE.getBoolean(); private final Set loadedWorlds; + private final NamespacedKey playerSplitKey; public StackingTask(UltimateStacker plugin) { this.plugin = plugin; + playerSplitKey = new NamespacedKey(plugin, "US_SPLIT_PLAYER"); this.stackManager = plugin.getEntityStackManager(); // Add loaded worlds. loadedWorlds = new HashSet<>(); @@ -182,6 +187,7 @@ public class StackingTask extends TimerTask { private boolean isEntityNotStackable(LivingEntity entity) { if (isMaxStack(entity)) return true; + if (isPersistentSplit(entity)) return true; // Make sure we have the correct entity type and that it is valid. if (!entity.isValid() @@ -255,6 +261,10 @@ public class StackingTask extends TimerTask { // Loop through our similar stackable entities. for (LivingEntity friendlyEntity : stackableFriends) { + if (isPersistentSplit(friendlyEntity)) { + continue; + } + // Process similar entities. EntityStack friendStack = stackManager.getStackedEntity(friendlyEntity); int amount = friendStack != null ? friendStack.getAmount() : 1; @@ -665,6 +675,22 @@ public class StackingTask extends TimerTask { return stack.getAmount() >= getEntityMaxStackSize(livingEntity); } + private boolean isPersistentSplit(LivingEntity entity) { + if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_14)) { + PersistentDataContainer container = entity.getPersistentDataContainer(); + if (container.has(playerSplitKey, PersistentDataType.BYTE)) { + processed.add(entity.getUniqueId()); + return true; + } + } else { + if (entity.hasMetadata("US_SPLIT_PLAYER")) { + processed.add(entity.getUniqueId()); + return true; + } + } + return false; + } + public boolean canFly(LivingEntity entity) { switch (entity.getType()) { case GHAST: From 91f4d2cf02f106785057ad54a1ab9b2c19b031a4 Mon Sep 17 00:00:00 2001 From: ceze88 Date: Wed, 7 Feb 2024 16:03:51 +0100 Subject: [PATCH 04/12] Release v3.1.1 --- UltimateStacker-API/pom.xml | 2 +- UltimateStacker-Plugin/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/UltimateStacker-API/pom.xml b/UltimateStacker-API/pom.xml index 70355c2..b90cd65 100644 --- a/UltimateStacker-API/pom.xml +++ b/UltimateStacker-API/pom.xml @@ -7,7 +7,7 @@ com.craftaro UltimateStacker-Parent - 3.1.0 + 3.1.1 UltimateStacker-API 1.0.0-SNAPSHOT diff --git a/UltimateStacker-Plugin/pom.xml b/UltimateStacker-Plugin/pom.xml index b69a9c7..8d14825 100644 --- a/UltimateStacker-Plugin/pom.xml +++ b/UltimateStacker-Plugin/pom.xml @@ -7,7 +7,7 @@ com.craftaro UltimateStacker-Parent - 3.1.0 + 3.1.1 UltimateStacker-Plugin diff --git a/pom.xml b/pom.xml index 1b0acf0..7fbc352 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.craftaro UltimateStacker-Parent pom - 3.1.0 + 3.1.1 UltimateStacker-API From 50d1a41acd4380f87b5a79421a7818d5a2adcff9 Mon Sep 17 00:00:00 2001 From: Christian Koop Date: Mon, 26 Feb 2024 12:13:45 +0100 Subject: [PATCH 05/12] ci: Fix potentially problematic `increment_version` value --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 0319e37caf0ece9a52e47ea04eb6d65e1d2abb26 Mon Sep 17 00:00:00 2001 From: Brianna OKeefe Date: Mon, 11 Mar 2024 19:07:11 -0500 Subject: [PATCH 06/12] Added support for 1.20's food for breeding compatibility. --- .../listeners/InteractListeners.java | 79 ++++++++++--------- 1 file changed, 43 insertions(+), 36 deletions(-) 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..e9082a8 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 @@ -8,13 +8,7 @@ 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; @@ -75,55 +69,68 @@ public class InteractListeners implements Listener { 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; } From f6ee159de331d3a83dea154a103a9943690756aa Mon Sep 17 00:00:00 2001 From: Brianna OKeefe Date: Mon, 11 Mar 2024 21:16:05 -0500 Subject: [PATCH 07/12] Added a bunch of new checks. --- UltimateStacker-Plugin/pom.xml | 2 +- .../stackable/entity/Check.java | 13 ++- .../tasks/StackingTask.java | 85 +++++++++++++------ 3 files changed, 71 insertions(+), 29 deletions(-) diff --git a/UltimateStacker-Plugin/pom.xml b/UltimateStacker-Plugin/pom.xml index 8d14825..1d62e49 100644 --- a/UltimateStacker-Plugin/pom.xml +++ b/UltimateStacker-Plugin/pom.xml @@ -128,7 +128,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/stackable/entity/Check.java b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/stackable/entity/Check.java index 90fee5b..f1b7633 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 @@ -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/tasks/StackingTask.java b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/tasks/StackingTask.java index 717d17a..fe2cb1d 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 @@ -17,32 +17,7 @@ import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.World; 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; @@ -634,6 +609,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; + } } } From 9fe86c728a4cb33fa28b2749b6ff8ff80467c0d7 Mon Sep 17 00:00:00 2001 From: Brianna OKeefe Date: Mon, 11 Mar 2024 21:52:21 -0500 Subject: [PATCH 08/12] Let's us javas max int. max --- .../stackable/item/StackedItemManagerImpl.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) 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(); From 7ea968ab28c4b88ab8be723db51a70e8ef94d2ef Mon Sep 17 00:00:00 2001 From: Brianna OKeefe Date: Tue, 12 Mar 2024 13:55:48 -0500 Subject: [PATCH 09/12] Let's add the new checks to the config. --- .../com.craftaro.ultimatestacker/settings/Settings.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 df39767..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 @@ -108,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()), From 19e0c36e1f20e67d4c93bedfc5cb3b9aa59e44cb Mon Sep 17 00:00:00 2001 From: Brianna OKeefe Date: Tue, 12 Mar 2024 13:55:59 -0500 Subject: [PATCH 10/12] Sheep color should be on by default. --- .../com.craftaro.ultimatestacker/stackable/entity/Check.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f1b7633..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), From 5f556b19803c455ee816d85b5b5fcaff736071dc Mon Sep 17 00:00:00 2001 From: Brianna O'Keefe Date: Tue, 12 Mar 2024 14:25:22 -0500 Subject: [PATCH 11/12] Performance improvements, fix for various breeding issues. --- UltimateStacker-Plugin/pom.xml | 7 + .../UltimateStacker.java | 57 +++----- .../listeners/BreedListeners.java | 29 ++-- .../listeners/DeathListeners.java | 6 +- .../listeners/InteractListeners.java | 8 +- .../stackable/entity/EntityStackImpl.java | 61 ++++---- .../tasks/BreedingTask.java | 48 ++++++ .../tasks/StackingTask.java | 137 +++++------------- pom.xml | 2 +- 9 files changed, 150 insertions(+), 205 deletions(-) create mode 100644 UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/tasks/BreedingTask.java diff --git a/UltimateStacker-Plugin/pom.xml b/UltimateStacker-Plugin/pom.xml index 1d62e49..95020c2 100644 --- a/UltimateStacker-Plugin/pom.xml +++ b/UltimateStacker-Plugin/pom.xml @@ -118,6 +118,13 @@ compile + + commons-lang + commons-lang + 2.6 + compile + + com.craftaro CraftaroCore 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 b2b5a65..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,6 +79,7 @@ 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; @@ -122,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(); @@ -154,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(); @@ -248,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(); @@ -264,10 +244,12 @@ public class UltimateStacker extends SongodaPlugin { } }); + //Start stacking task if (Settings.STACK_ENTITIES.getBoolean()) { - //Start stacking task + 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) -> { @@ -275,9 +257,8 @@ public class UltimateStacker extends SongodaPlugin { this.blockStackManager.addBlock(blockStack); if (useBlockHolo) { if (blockStack == null) return; - if (blockStack.getLocation().getWorld() != null) { + if (blockStack.getLocation().getWorld() != null) updateHologram(blockStack); - } } }); } @@ -307,9 +288,9 @@ public class UltimateStacker extends SongodaPlugin { this.setLocale(getConfig().getString("System.Language Mode"), true); this.locale.reloadMessages(); - if (stackingTask != null) { - this.stackingTask.stop(); - } + if (stackingTask != null) + this.stackingTask.cancel(); + if (Settings.STACK_ENTITIES.getBoolean()) { this.stackingTask = new StackingTask(this); } @@ -474,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/listeners/BreedListeners.java b/UltimateStacker-Plugin/src/main/java/com.craftaro.ultimatestacker/listeners/BreedListeners.java index 700c24d..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 @@ -2,47 +2,38 @@ package com.craftaro.ultimatestacker.listeners; import com.craftaro.ultimatestacker.UltimateStacker; import com.craftaro.ultimatestacker.api.stack.entity.EntityStack; -import org.bukkit.Bukkit; +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) { - //TODO: Fix breed. It removes a entity from the stack but not spawn a new one. (Splitting mechanic) - boolean isMotherStacked = plugin.getEntityStackManager().isStackedEntity(event.getMother()); - boolean isFatherStacked = plugin.getEntityStackManager().isStackedEntity(event.getFather()); + EntityStack stackedMother = entityStackManager.getStackedEntity(event.getMother()); + EntityStack stackedFather = entityStackManager.getStackedEntity(event.getFather()); - if (!isMotherStacked && !isFatherStacked) return; + plugin.getBreedingTask().addBreedingTicket(event.getMother(), event.getFather()); - if (isMotherStacked) { - EntityStack stack = plugin.getEntityStackManager().getStackedEntity(event.getMother()); + if (stackedMother != null) { + EntityStack stack = entityStackManager.getStackedEntity(event.getMother()); if (stack.getAmount() <= 1) return; stack.releaseHost(); } - if (isFatherStacked) { - EntityStack stack = plugin.getEntityStackManager().getStackedEntity(event.getFather()); + if (stackedFather != null) { + EntityStack stack = entityStackManager.getStackedEntity(event.getFather()); if (stack.getAmount() <= 1) return; stack.releaseHost(); } - - 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); } } 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 e9082a8..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,7 +1,5 @@ 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; @@ -58,12 +56,8 @@ 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); } } 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 f54c84f..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,6 @@ 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; @@ -27,8 +24,6 @@ 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; @@ -42,6 +37,7 @@ public class EntityStackImpl implements EntityStack { /** * 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) { @@ -85,6 +81,7 @@ public class EntityStackImpl implements EntityStack { /** * 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. */ @@ -240,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(); @@ -252,35 +249,28 @@ public class EntityStackImpl implements EntityStack { @Override public synchronized void releaseHost() { - //Remove the metadata from the entity if it's the last one -// if (getAmount() == 1) { -// if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_14)) { -// PersistentDataContainer container = hostEntity.getPersistentDataContainer(); -// container.remove(STACKED_ENTITY_KEY); -// } else { -// hostEntity.removeMetadata("US_AMOUNT", plugin); -// } -// hostEntity.setCustomName(null); -// hostEntity.setCustomNameVisible(false); -// return; -// } + wipeData(); - LivingEntity oldHost = hostEntity; - if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_14)) { - PersistentDataContainer container = oldHost.getPersistentDataContainer(); - container.remove(STACKED_ENTITY_KEY); - //Add new entry that the entity was split by player, not to stack it anymore - container.set(new NamespacedKey(plugin, "US_SPLIT_PLAYER"), PersistentDataType.BYTE, (byte) 1); - } else { - oldHost.removeMetadata("US_AMOUNT", plugin); - //Add new entry that the entity was split by player, not to stack it anymore - oldHost.setMetadata("US_SPLIT_PLAYER", new FixedMetadataValue(plugin, true)); - } - - //Summon a new entity and update the stack and remove the metadata from the old entity + //Summon a new entity, update the stack and remove the metadata from the old entity this.hostEntity = takeOneAndSpawnEntity(hostEntity.getLocation()); - setAmount(amount-1); - updateNameTag(); + if (amount == 2) { + wipeData(); + } else { + 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); + } } @Override @@ -291,9 +281,8 @@ 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/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 fe2cb1d..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,45 +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.NamespacedKey; -import org.bukkit.World; +import org.bukkit.*; import org.bukkit.configuration.ConfigurationSection; 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.Comparator; -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.TreeSet; -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(r -> new Thread(r, "UltimateStacker-Stacking-Thread")); - private final EntityStackManager stackManager; + private final BreedingTask breedingTask; private final ConfigurationSection configurationSection = UltimateStacker.getInstance().getMobFile(); private final List processed = new ArrayList<>(); @@ -70,12 +49,12 @@ public class StackingTask extends TimerTask { onlyStackOnSurface = Settings.ONLY_STACK_ON_SURFACE.getBoolean(); private final Set loadedWorlds; - private final NamespacedKey playerSplitKey; public StackingTask(UltimateStacker plugin) { this.plugin = plugin; - playerSplitKey = new NamespacedKey(plugin, "US_SPLIT_PLAYER"); - this.stackManager = plugin.getEntityStackManager(); + stackManager = plugin.getEntityStackManager(); + breedingTask = plugin.getBreedingTask(); + // Add loaded worlds. loadedWorlds = new HashSet<>(); for (World world : Bukkit.getWorlds()) { @@ -83,12 +62,9 @@ public class StackingTask extends TimerTask { if (isWorldDisabled(world)) continue; loadedWorlds.add(new SWorld(world)); } - // Start the stacking task. - 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 @@ -99,42 +75,33 @@ public class StackingTask extends TimerTask { for (SWorld sWorld : loadedWorlds) { List entities; // Get the loaded entities from the current world and reverse them. - try { - entities = getLivingEntitiesSync(sWorld).get(); - } catch (ExecutionException | InterruptedException ex) { - ex.printStackTrace(); - continue; - } + 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) { + if (flag != null && !flag) entities.removeIf(entity1 -> entity1.getUniqueId().equals(entity.getUniqueId())); - } } - 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; - // 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, location); - } - }); + // Process the entity. + processEntity(entity, location, entities); + } } - } catch (Exception ignored) { - + } catch (Exception e) { + e.printStackTrace(); } finally { // Make sure we clear the processed list. this.processed.clear(); @@ -142,18 +109,6 @@ public class StackingTask extends TimerTask { } } - 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)); } @@ -162,23 +117,17 @@ public class StackingTask extends TimerTask { private boolean isEntityNotStackable(LivingEntity entity) { if (isMaxStack(entity)) return true; - if (isPersistentSplit(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")) { + // 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")) { + if (!configurationSection.getBoolean("Mobs." + entity.getType().name() + ".Enabled")) return true; - } // 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() @@ -186,9 +135,8 @@ public class StackingTask extends TimerTask { List stackReasons; if (onlyStackFromSpawners) { // If only stack from spawners is enabled, make sure the entity spawned from a spawner. - if (!"SPAWNER".equals(spawnReason)) { + if (!"SPAWNER".equals(spawnReason)) return true; - } } else if (!(stackReasons = this.stackReasons).isEmpty() && !stackReasons.contains(spawnReason)) { // Only stack if on the list of events to stack return true; @@ -199,7 +147,7 @@ public class StackingTask extends TimerTask { return onlyStackOnSurface && canFly(entity) && !entity.getType().name().equals("SHULKER") && !entity.isOnGround() && !(ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13) && (entity).isSwimming()); } - private void processEntity(LivingEntity baseEntity, Location location) { + 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); @@ -236,9 +184,8 @@ public class StackingTask extends TimerTask { // Loop through our similar stackable entities. for (LivingEntity friendlyEntity : stackableFriends) { - if (isPersistentSplit(friendlyEntity)) { + if (!entities.contains(friendlyEntity)) continue; - } // Process similar entities. EntityStack friendStack = stackManager.getStackedEntity(friendlyEntity); @@ -271,7 +218,8 @@ public class StackingTask extends TimerTask { /** * This method splitting overstacked entities into new stacks. * Must be called synchronously. - * @param baseStack The base stack to check for splitting. + * + * @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. */ @@ -336,12 +284,13 @@ public class StackingTask extends TimerTask { /** * Get all entities around an entity within a radius which are similar to the entity. + * * @param entity The entity to get similar entities around. * @return A list of similar entities around the entity. */ public List getFriendlyStacksNearby(LivingEntity entity) { if (!stackWholeChunk) { - return entity.getNearbyEntities(searchRadius/2.0, searchRadius/2.0, searchRadius/2.0) + 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()); } @@ -708,22 +657,6 @@ public class StackingTask extends TimerTask { return stack.getAmount() >= getEntityMaxStackSize(livingEntity); } - private boolean isPersistentSplit(LivingEntity entity) { - if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_14)) { - PersistentDataContainer container = entity.getPersistentDataContainer(); - if (container.has(playerSplitKey, PersistentDataType.BYTE)) { - processed.add(entity.getUniqueId()); - return true; - } - } else { - if (entity.hasMetadata("US_SPLIT_PLAYER")) { - processed.add(entity.getUniqueId()); - return true; - } - } - return false; - } - public boolean canFly(LivingEntity entity) { switch (entity.getType()) { case GHAST: diff --git a/pom.xml b/pom.xml index 7fbc352..ac30344 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ https://craftaro.com/marketplace/product/16 - 3.0.0-SNAPSHOT + 3.0.1-SNAPSHOT 8 1.8 From 5c056ec545149e28fbac8c13ee4bdf92eee2d8a0 Mon Sep 17 00:00:00 2001 From: Brianna O'Keefe Date: Tue, 12 Mar 2024 14:26:24 -0500 Subject: [PATCH 12/12] version 3.1.2 --- UltimateStacker-API/pom.xml | 2 +- UltimateStacker-Plugin/pom.xml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/UltimateStacker-API/pom.xml b/UltimateStacker-API/pom.xml index b90cd65..d2914f8 100644 --- a/UltimateStacker-API/pom.xml +++ b/UltimateStacker-API/pom.xml @@ -7,7 +7,7 @@ com.craftaro UltimateStacker-Parent - 3.1.1 + 3.1.2 UltimateStacker-API 1.0.0-SNAPSHOT diff --git a/UltimateStacker-Plugin/pom.xml b/UltimateStacker-Plugin/pom.xml index 95020c2..62980ac 100644 --- a/UltimateStacker-Plugin/pom.xml +++ b/UltimateStacker-Plugin/pom.xml @@ -7,7 +7,7 @@ com.craftaro UltimateStacker-Parent - 3.1.1 + 3.1.2 UltimateStacker-Plugin diff --git a/pom.xml b/pom.xml index ac30344..e03a41d 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.craftaro UltimateStacker-Parent pom - 3.1.1 + 3.1.2 UltimateStacker-API