Fix Stacking Task, fix loot tables for non stacked mobs. Add last player damage to mobs. Performance improvements.
This commit is contained in:
parent
9f77b48974
commit
1142fb0e6a
|
@ -78,7 +78,12 @@ public class EntityListeners implements Listener {
|
||||||
|
|
||||||
Entity entity = event.getEntity();
|
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()
|
&& Settings.DISABLE_KNOCKBACK.getBoolean()
|
||||||
&& ((Player) event.getDamager()).getItemInHand().getEnchantmentLevel(Enchantment.KNOCKBACK) == 0) {
|
&& ((Player) event.getDamager()).getItemInHand().getEnchantmentLevel(Enchantment.KNOCKBACK) == 0) {
|
||||||
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> {
|
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> {
|
||||||
|
|
|
@ -116,12 +116,23 @@ public class EntityStack extends StackedEntity {
|
||||||
killed.setCustomName(null);
|
killed.setCustomName(null);
|
||||||
killed.setCustomNameVisible(false);
|
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()
|
boolean killWholeStack = Settings.KILL_WHOLE_STACK_ON_DEATH.getBoolean()
|
||||||
|| plugin.getMobFile().getBoolean("Mobs." + killed.getType().name() + ".Kill Whole Stack");
|
|| plugin.getMobFile().getBoolean("Mobs." + killed.getType().name() + ".Kill Whole Stack");
|
||||||
|
|
||||||
if (killWholeStack && getAmount() != 1) {
|
if (killWholeStack && getAmount() > 1) {
|
||||||
handleWholeStackDeath(killed, drops, custom, droppedExp, event);
|
handleWholeStackDeath(killed, drops, custom, droppedExp, event);
|
||||||
} else if (getAmount() != 1) {
|
return;
|
||||||
|
} else if (getAmount() > 1) {
|
||||||
List<String> reasons = Settings.INSTANT_KILL.getStringList();
|
List<String> reasons = Settings.INSTANT_KILL.getStringList();
|
||||||
EntityDamageEvent lastDamageCause = killed.getLastDamageCause();
|
EntityDamageEvent lastDamageCause = killed.getLastDamageCause();
|
||||||
|
|
||||||
|
@ -134,6 +145,7 @@ public class EntityStack extends StackedEntity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
System.err.println("Single death");
|
||||||
handleSingleStackDeath(killed, drops, droppedExp, event);
|
handleSingleStackDeath(killed, drops, droppedExp, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@ import org.bukkit.World;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.EntityType;
|
import org.bukkit.entity.EntityType;
|
||||||
import org.bukkit.entity.LivingEntity;
|
import org.bukkit.entity.LivingEntity;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.metadata.FixedMetadataValue;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -38,6 +40,19 @@ public class EntityStackManager {
|
||||||
return entity.getMetadata("US_AMOUNT").get(0).asInt();
|
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) {
|
public EntityStack addStack(LivingEntity entity) {
|
||||||
return addStack(entity, getAmount(entity) == 1 ? 1 : getAmount(entity));
|
return addStack(entity, getAmount(entity) == 1 ? 1 : getAmount(entity));
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,25 +44,31 @@ import org.bukkit.entity.TropicalFish;
|
||||||
import org.bukkit.entity.Villager;
|
import org.bukkit.entity.Villager;
|
||||||
import org.bukkit.entity.Wolf;
|
import org.bukkit.entity.Wolf;
|
||||||
import org.bukkit.entity.Zombie;
|
import org.bukkit.entity.Zombie;
|
||||||
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||||
import org.bukkit.inventory.EntityEquipment;
|
import org.bukkit.inventory.EntityEquipment;
|
||||||
import org.bukkit.scheduler.BukkitRunnable;
|
import org.bukkit.scheduler.BukkitRunnable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.TimerTask;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
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 UltimateStacker plugin;
|
||||||
private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
|
private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
@ -99,7 +105,7 @@ public class StackingTask implements Runnable {
|
||||||
|
|
||||||
// Start the stacking task.
|
// Start the stacking task.
|
||||||
//runTaskTimerAsynchronously(plugin, 0, Settings.STACK_SEARCH_TICK_SPEED.getInt());
|
//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() {
|
public void stop() {
|
||||||
|
@ -108,44 +114,48 @@ public class StackingTask implements Runnable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// Should entities be stacked?
|
//make sure if the task running if any error occurs
|
||||||
if (!Settings.STACK_ENTITIES.getBoolean()) return;
|
try {
|
||||||
|
// Should entities be stacked?
|
||||||
|
if (!Settings.STACK_ENTITIES.getBoolean()) return;
|
||||||
|
|
||||||
// Loop through each world.
|
// Loop through each world.
|
||||||
for (SWorld sWorld : loadedWorlds) {
|
for (SWorld sWorld : loadedWorlds) {
|
||||||
// If world is disabled then continue to the next world.
|
// If world is disabled then continue to the next world.
|
||||||
if (isWorldDisabled(sWorld.getWorld())) continue;
|
if (isWorldDisabled(sWorld.getWorld())) continue;
|
||||||
|
|
||||||
// Get the loaded entities from the current world and reverse them.
|
// Get the loaded entities from the current world and reverse them.
|
||||||
List<LivingEntity> entities;
|
List<LivingEntity> entities;
|
||||||
try {
|
try {
|
||||||
entities = getLivingEntitiesSync(sWorld).get();
|
entities = getLivingEntitiesSync(sWorld).get();
|
||||||
} catch (ExecutionException | InterruptedException ex) {
|
} catch (ExecutionException | InterruptedException ex) {
|
||||||
ex.printStackTrace();
|
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))
|
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
Collections.reverse(entities);
|
||||||
|
|
||||||
// Make sure our entity has not already been processed.
|
// Loop through the entities.
|
||||||
// Skip it if it has been.
|
for (LivingEntity entity : entities) {
|
||||||
if (this.processed.contains(entity.getUniqueId())) continue;
|
// 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.
|
// Check to see if entity is not stackable.
|
||||||
this.processEntity(entity, sWorld, location);
|
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.
|
||||||
// Clear caches in preparation for the next run.
|
this.processed.clear();
|
||||||
this.processed.clear();
|
this.cachedChunks.clear();
|
||||||
this.cachedChunks.clear();
|
} catch (Exception ignored) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Future<List<LivingEntity>> getLivingEntitiesSync(SWorld sWorld) {
|
private Future<List<LivingEntity>> 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 only stack from spawners is enabled, make sure the entity spawned from a spawner.
|
||||||
if (!"SPAWNER".equals(spawnReason))
|
if (!"SPAWNER".equals(spawnReason))
|
||||||
return false;
|
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
|
// Only stack if on the list of events to stack
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Cast our entity to living entity.
|
// Cast our entity to living entity.
|
||||||
LivingEntity livingEntity = (LivingEntity) entity;
|
LivingEntity livingEntity = (LivingEntity) entity;
|
||||||
|
@ -224,6 +235,7 @@ public class StackingTask implements Runnable {
|
||||||
|
|
||||||
if (isStack && baseStack.getAmount() == maxEntityStackSize) {
|
if (isStack && baseStack.getAmount() == maxEntityStackSize) {
|
||||||
// If the stack is already at the max size then we can skip it.
|
// If the stack is already at the max size then we can skip it.
|
||||||
|
processed.add(baseEntity.getUniqueId());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,10 +248,10 @@ public class StackingTask implements Runnable {
|
||||||
// If this entity is named, a custom entity or disabled then skip it.
|
// If this entity is named, a custom entity or disabled then skip it.
|
||||||
if (!isStack && (baseEntity.getCustomName() != null
|
if (!isStack && (baseEntity.getCustomName() != null
|
||||||
&& plugin.getCustomEntityManager().getCustomEntity(baseEntity) == 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;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Get similar entities around our entity and make sure those entities are both compatible and stackable.
|
// Get similar entities around our entity and make sure those entities are both compatible and stackable.
|
||||||
List<LivingEntity> stackableFriends = new LinkedList<>();
|
List<LivingEntity> stackableFriends = new LinkedList<>();
|
||||||
|
@ -258,24 +270,24 @@ public class StackingTask implements Runnable {
|
||||||
|
|
||||||
// Get this entities friendStack.
|
// Get this entities friendStack.
|
||||||
EntityStack friendStack = stackManager.getStack(friendlyEntity);
|
EntityStack friendStack = stackManager.getStack(friendlyEntity);
|
||||||
|
int amount = friendStack != null ? friendStack.getAmount() : 1;
|
||||||
if (friendStack == null) continue;
|
|
||||||
|
|
||||||
// Check to see if this friendlyEntity is stacked and friendStack plus
|
// Check to see if this friendlyEntity is stacked and friendStack plus
|
||||||
// our amount to stack is not above our max friendStack size
|
// our amount to stack is not above our max friendStack size
|
||||||
// for this friendlyEntity.
|
// for this friendlyEntity.
|
||||||
|
|
||||||
boolean overstack = (friendStack.getAmount() + amountToStack) > maxEntityStackSize;
|
boolean overstack = (amount + amountToStack) > maxEntityStackSize;
|
||||||
|
|
||||||
if (!overstack) {
|
if (!overstack) {
|
||||||
friendStack.setAmount(friendStack.getAmount() + amountToStack);
|
stackManager.createStack(friendlyEntity, amount + amountToStack);
|
||||||
if (baseEntity.isLeashed())
|
|
||||||
Bukkit.getScheduler().runTask(plugin, () -> baseEntity.getWorld()
|
|
||||||
.dropItemNaturally(baseEntity.getLocation(), CompatibleMaterial.LEAD.getItem()));
|
|
||||||
if (baseStack != null) {
|
|
||||||
baseStack.destroy();
|
|
||||||
}
|
|
||||||
processed.add(baseEntity.getUniqueId());
|
processed.add(baseEntity.getUniqueId());
|
||||||
|
|
||||||
|
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||||
|
if (baseEntity.isLeashed()) {
|
||||||
|
baseEntity.getWorld().dropItemNaturally(baseEntity.getLocation(), CompatibleMaterial.LEAD.getItem());
|
||||||
|
}
|
||||||
|
baseEntity.remove();
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -301,12 +313,16 @@ public class StackingTask implements Runnable {
|
||||||
} while (finalStackSize >= 0);
|
} while (finalStackSize >= 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Remove our entity and mark it as processed.
|
//Mark it as processed.
|
||||||
processed.add(livingEntity.getUniqueId());
|
processed.add(livingEntity.getUniqueId());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<CachedChunk> getNearbyChunks(SWorld sWorld, Location location, double radius, boolean singleChunk) {
|
private Set<CachedChunk> 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();
|
World world = location.getWorld();
|
||||||
Set<CachedChunk> chunks = new HashSet<>();
|
Set<CachedChunk> chunks = new HashSet<>();
|
||||||
if (world == null) return chunks;
|
if (world == null) return chunks;
|
||||||
|
@ -330,55 +346,31 @@ public class StackingTask implements Runnable {
|
||||||
return chunks;
|
return chunks;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<LivingEntity> 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<LivingEntity> getFriendlyStacksNearby(LivingEntity entity, double radius, boolean singleChunk) {
|
||||||
List<LivingEntity> entities = new ArrayList<>();
|
List<LivingEntity> entities = new ArrayList<>();
|
||||||
for (CachedChunk chunk : getNearbyChunks(sWorld, location, radius, singleChunk)) {
|
Set<CachedChunk> chunks = getNearbyChunks(new SWorld(entity.getWorld()), entity.getLocation(), radius, singleChunk);
|
||||||
if (chunk == null) continue;
|
for (CachedChunk chunk : chunks) {
|
||||||
Entity[] entityArray;
|
Entity[] entityList = chunk.getEntities();
|
||||||
if (cachedChunks.containsKey(chunk)) {
|
for (Entity e : entityList) {
|
||||||
entityArray = cachedChunks.get(chunk);
|
if (!processed.contains(e.getUniqueId()) && e.getType() == entity.getType() && e instanceof LivingEntity && e.isValid() && e.getLocation().distance(entity.getLocation()) <= radius) {
|
||||||
} else {
|
entities.add((LivingEntity) e);
|
||||||
try {
|
|
||||||
entityArray = getEntitiesInChunkSync(chunk).get();
|
|
||||||
cachedChunks.put(chunk, entityArray);
|
|
||||||
} catch (ExecutionException | InterruptedException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
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<LivingEntity> getSimilarEntitiesAroundEntity(LivingEntity initialEntity, SWorld sWorld, Location location) {
|
public List<LivingEntity> getSimilarEntitiesAroundEntity(LivingEntity initialEntity, SWorld sWorld, Location location) {
|
||||||
// Create a list of all entities around the initial entity of the same type.
|
// Create a list of all entities around the initial entity of the same type.
|
||||||
List<LivingEntity> entityList = new LinkedList<>();
|
List<LivingEntity> entityList = new LinkedList<>(getFriendlyStacksNearby(initialEntity, searchRadius, stackWholeChunk));
|
||||||
|
|
||||||
for (LivingEntity entity : getNearbyEntities(sWorld, location, searchRadius, stackWholeChunk)) {
|
|
||||||
if (entity.getType() != initialEntity.getType() || entity == initialEntity)
|
|
||||||
continue;
|
|
||||||
entityList.add(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomEntity customEntity = plugin.getCustomEntityManager().getCustomEntity(initialEntity);
|
CustomEntity customEntity = plugin.getCustomEntityManager().getCustomEntity(initialEntity);
|
||||||
if (customEntity != null)
|
if (customEntity != null)
|
||||||
|
|
Loading…
Reference in New Issue