From 1142fb0e6a3cb913617bedb1e6bbbf728d96cdee Mon Sep 17 00:00:00 2001 From: ceze88 Date: Wed, 22 Feb 2023 09:59:28 +0100 Subject: [PATCH] Fix Stacking Task, fix loot tables for non stacked mobs. Add last player damage to mobs. Performance improvements. --- .../listeners/entity/EntityListeners.java | 7 +- .../stackable/entity/EntityStack.java | 16 +- .../stackable/entity/EntityStackManager.java | 15 ++ .../ultimatestacker/tasks/StackingTask.java | 170 +++++++++--------- 4 files changed, 116 insertions(+), 92 deletions(-) diff --git a/src/main/java/com/songoda/ultimatestacker/listeners/entity/EntityListeners.java b/src/main/java/com/songoda/ultimatestacker/listeners/entity/EntityListeners.java index fe3b937..a222fc8 100644 --- a/src/main/java/com/songoda/ultimatestacker/listeners/entity/EntityListeners.java +++ b/src/main/java/com/songoda/ultimatestacker/listeners/entity/EntityListeners.java @@ -78,7 +78,12 @@ public class EntityListeners implements Listener { Entity entity = event.getEntity(); - if (entity instanceof LivingEntity && plugin.getEntityStackManager().isStackedEntity(entity) + if (!(entity instanceof LivingEntity)) return; + if (event.getDamager() instanceof Player) { + plugin.getEntityStackManager().setLastPlayerDamage(entity, (Player) event.getDamager()); + } + + if (plugin.getEntityStackManager().isStackedEntity(entity) && Settings.DISABLE_KNOCKBACK.getBoolean() && ((Player) event.getDamager()).getItemInHand().getEnchantmentLevel(Enchantment.KNOCKBACK) == 0) { Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> { diff --git a/src/main/java/com/songoda/ultimatestacker/stackable/entity/EntityStack.java b/src/main/java/com/songoda/ultimatestacker/stackable/entity/EntityStack.java index 731c574..c34198e 100644 --- a/src/main/java/com/songoda/ultimatestacker/stackable/entity/EntityStack.java +++ b/src/main/java/com/songoda/ultimatestacker/stackable/entity/EntityStack.java @@ -116,12 +116,23 @@ public class EntityStack extends StackedEntity { killed.setCustomName(null); killed.setCustomNameVisible(false); + //replace %player% in drop commands with the last player to damage the entity + String lastDamage = plugin.getEntityStackManager().getLastPlayerDamage(killed); + if (lastDamage != null) { + drops.forEach(drop -> { + if (drop.getCommand() != null) { + drop.setCommand(drop.getCommand().replace("%player%", lastDamage)); + } + }); + } + boolean killWholeStack = Settings.KILL_WHOLE_STACK_ON_DEATH.getBoolean() || plugin.getMobFile().getBoolean("Mobs." + killed.getType().name() + ".Kill Whole Stack"); - if (killWholeStack && getAmount() != 1) { + if (killWholeStack && getAmount() > 1) { handleWholeStackDeath(killed, drops, custom, droppedExp, event); - } else if (getAmount() != 1) { + return; + } else if (getAmount() > 1) { List reasons = Settings.INSTANT_KILL.getStringList(); EntityDamageEvent lastDamageCause = killed.getLastDamageCause(); @@ -134,6 +145,7 @@ public class EntityStack extends StackedEntity { } } } + System.err.println("Single death"); handleSingleStackDeath(killed, drops, droppedExp, event); } diff --git a/src/main/java/com/songoda/ultimatestacker/stackable/entity/EntityStackManager.java b/src/main/java/com/songoda/ultimatestacker/stackable/entity/EntityStackManager.java index 3b1b508..eec6b6b 100644 --- a/src/main/java/com/songoda/ultimatestacker/stackable/entity/EntityStackManager.java +++ b/src/main/java/com/songoda/ultimatestacker/stackable/entity/EntityStackManager.java @@ -9,6 +9,8 @@ import org.bukkit.World; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.metadata.FixedMetadataValue; import java.util.Collection; import java.util.Collections; @@ -38,6 +40,19 @@ public class EntityStackManager { return entity.getMetadata("US_AMOUNT").get(0).asInt(); } + public String getLastPlayerDamage(Entity entity) { + if (!entity.hasMetadata("US_LAST_PLAYER_DAMAGE")) return null; + if (entity.getMetadata("US_LAST_PLAYER_DAMAGE").isEmpty()) return null; + return entity.getMetadata("US_LAST_PLAYER_DAMAGE").get(0).asString(); + } + + public void setLastPlayerDamage(Entity entity, Player player) { + if (player == null) return; + if (entity == null) return; + if (entity instanceof Player) return; + entity.setMetadata("US_LAST_PLAYER_DAMAGE", new FixedMetadataValue(plugin, player.getName())); + } + public EntityStack addStack(LivingEntity entity) { return addStack(entity, getAmount(entity) == 1 ? 1 : getAmount(entity)); } diff --git a/src/main/java/com/songoda/ultimatestacker/tasks/StackingTask.java b/src/main/java/com/songoda/ultimatestacker/tasks/StackingTask.java index e80e63b..e665ce7 100644 --- a/src/main/java/com/songoda/ultimatestacker/tasks/StackingTask.java +++ b/src/main/java/com/songoda/ultimatestacker/tasks/StackingTask.java @@ -44,25 +44,31 @@ import org.bukkit.entity.TropicalFish; import org.bukkit.entity.Villager; import org.bukkit.entity.Wolf; import org.bukkit.entity.Zombie; +import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.inventory.EntityEquipment; import org.bukkit.scheduler.BukkitRunnable; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TimerTask; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; -public class StackingTask implements Runnable { +public class StackingTask extends TimerTask { private final UltimateStacker plugin; private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); @@ -99,7 +105,7 @@ public class StackingTask implements Runnable { // Start the stacking task. //runTaskTimerAsynchronously(plugin, 0, Settings.STACK_SEARCH_TICK_SPEED.getInt()); - executorService.scheduleAtFixedRate(this, 0, Settings.STACK_SEARCH_TICK_SPEED.getInt()*20L, java.util.concurrent.TimeUnit.MILLISECONDS); + executorService.scheduleAtFixedRate(this, 0, (Settings.STACK_SEARCH_TICK_SPEED.getInt()*50L), TimeUnit.MILLISECONDS); } public void stop() { @@ -108,44 +114,48 @@ public class StackingTask implements Runnable { @Override public void run() { - // Should entities be stacked? - if (!Settings.STACK_ENTITIES.getBoolean()) return; + //make sure if the task running if any error 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; + // Loop through each world. + for (SWorld sWorld : loadedWorlds) { + // If world is disabled then continue to the next world. + if (isWorldDisabled(sWorld.getWorld())) continue; - // Get the loaded entities from the current world and reverse them. - List entities; - try { - entities = getLivingEntitiesSync(sWorld).get(); - } catch (ExecutionException | InterruptedException ex) { - ex.printStackTrace(); - continue; - } - Collections.reverse(entities); - - // Loop through the entities. - for (LivingEntity entity : entities) { - // Get entity location to pass around as its faster this way. - Location location = entity.getLocation(); - - // Check to see if entity is not stackable. - if (!isEntityStackable(entity)) + // Get the loaded entities from the current world and reverse them. + List entities; + try { + entities = getLivingEntitiesSync(sWorld).get(); + } catch (ExecutionException | InterruptedException ex) { + ex.printStackTrace(); continue; + } + Collections.reverse(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 (this.processed.contains(entity.getUniqueId())) continue; - // Process the entity. - this.processEntity(entity, sWorld, location); + // 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); + } } - } - // Clear caches in preparation for the next run. - this.processed.clear(); - this.cachedChunks.clear(); + // Clear caches in preparation for the next run. + this.processed.clear(); + this.cachedChunks.clear(); + } catch (Exception ignored) {} } private Future> getLivingEntitiesSync(SWorld sWorld) { @@ -186,9 +196,10 @@ public class StackingTask implements Runnable { // If only stack from spawners is enabled, make sure the entity spawned from a spawner. if (!"SPAWNER".equals(spawnReason)) return false; - } else if (!(stackReasons = this.stackReasons).isEmpty() && !stackReasons.contains(spawnReason)) + } else if (!(stackReasons = this.stackReasons).isEmpty() && !stackReasons.contains(spawnReason)) { // Only stack if on the list of events to stack return false; + } // Cast our entity to living entity. LivingEntity livingEntity = (LivingEntity) entity; @@ -224,6 +235,7 @@ public class StackingTask implements Runnable { if (isStack && baseStack.getAmount() == maxEntityStackSize) { // If the stack is already at the max size then we can skip it. + processed.add(baseEntity.getUniqueId()); return; } @@ -236,10 +248,10 @@ public class StackingTask implements Runnable { // 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")) + || !configurationSection.getBoolean("Mobs." + baseEntity.getType().name() + ".Enabled")) { + 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<>(); @@ -258,24 +270,24 @@ public class StackingTask implements Runnable { // Get this entities friendStack. EntityStack friendStack = stackManager.getStack(friendlyEntity); - - if (friendStack == null) continue; + 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 = (friendStack.getAmount() + amountToStack) > maxEntityStackSize; + boolean overstack = (amount + amountToStack) > maxEntityStackSize; if (!overstack) { - friendStack.setAmount(friendStack.getAmount() + amountToStack); - if (baseEntity.isLeashed()) - Bukkit.getScheduler().runTask(plugin, () -> baseEntity.getWorld() - .dropItemNaturally(baseEntity.getLocation(), CompatibleMaterial.LEAD.getItem())); - if (baseStack != null) { - baseStack.destroy(); - } + stackManager.createStack(friendlyEntity, amount + amountToStack); processed.add(baseEntity.getUniqueId()); + + Bukkit.getScheduler().runTask(plugin, () -> { + if (baseEntity.isLeashed()) { + baseEntity.getWorld().dropItemNaturally(baseEntity.getLocation(), CompatibleMaterial.LEAD.getItem()); + } + baseEntity.remove(); + }); return; } } @@ -301,12 +313,16 @@ public class StackingTask implements Runnable { } while (finalStackSize >= 0); }); - // Remove our entity and mark it as processed. + //Mark it as processed. processed.add(livingEntity.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()))); + } World world = location.getWorld(); Set chunks = new HashSet<>(); if (world == null) return chunks; @@ -330,55 +346,31 @@ public class StackingTask implements Runnable { return chunks; } - private List getNearbyEntities(SWorld sWorld, Location location, double radius, boolean singleChunk) { + /** + * 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) { List entities = new ArrayList<>(); - for (CachedChunk chunk : getNearbyChunks(sWorld, location, radius, singleChunk)) { - if (chunk == null) continue; - Entity[] entityArray; - if (cachedChunks.containsKey(chunk)) { - entityArray = cachedChunks.get(chunk); - } else { - try { - entityArray = getEntitiesInChunkSync(chunk).get(); - cachedChunks.put(chunk, entityArray); - } catch (ExecutionException | InterruptedException ex) { - ex.printStackTrace(); - continue; + Set chunks = getNearbyChunks(new SWorld(entity.getWorld()), entity.getLocation(), radius, singleChunk); + 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) { + entities.add((LivingEntity) e); } } - - if (entityArray == null) continue; - - for (Entity e : entityArray) { - if (e == null) continue; - if (e.getWorld() != location.getWorld() - || !(e instanceof LivingEntity) - || (!singleChunk && location.distanceSquared(e.getLocation()) >= radius * radius)) continue; - entities.add((LivingEntity) e); - } } - + entities.removeIf(entity1 -> entity1.equals(entity)); return entities; } - public int getSimilarStacksInChunk(SWorld sWorld, LivingEntity entity) { - int count = 0; - for (LivingEntity e : getNearbyEntities(sWorld, entity.getLocation(), -1, true)) { - if (entity.getType() == e.getType() && plugin.getEntityStackManager().isStackedEntity(e)) - count++; - } - return count; - } - public List getSimilarEntitiesAroundEntity(LivingEntity initialEntity, SWorld sWorld, Location location) { // Create a list of all entities around the initial entity of the same type. - List entityList = new LinkedList<>(); - - for (LivingEntity entity : getNearbyEntities(sWorld, location, searchRadius, stackWholeChunk)) { - if (entity.getType() != initialEntity.getType() || entity == initialEntity) - continue; - entityList.add(entity); - } + List entityList = new LinkedList<>(getFriendlyStacksNearby(initialEntity, searchRadius, stackWholeChunk)); CustomEntity customEntity = plugin.getCustomEntityManager().getCustomEntity(initialEntity); if (customEntity != null)