2018-11-06 04:33:10 +01:00
|
|
|
package com.songoda.ultimatestacker.tasks;
|
|
|
|
|
|
|
|
import com.songoda.ultimatestacker.UltimateStacker;
|
|
|
|
import com.songoda.ultimatestacker.entity.EntityStack;
|
|
|
|
import com.songoda.ultimatestacker.entity.EntityStackManager;
|
2019-09-03 23:22:15 +02:00
|
|
|
import com.songoda.ultimatestacker.settings.Setting;
|
2018-11-06 04:33:10 +01:00
|
|
|
import com.songoda.ultimatestacker.utils.Methods;
|
|
|
|
import org.bukkit.Bukkit;
|
2019-07-31 21:02:05 +02:00
|
|
|
import org.bukkit.Location;
|
2018-11-06 04:33:10 +01:00
|
|
|
import org.bukkit.World;
|
|
|
|
import org.bukkit.configuration.ConfigurationSection;
|
2019-08-08 15:51:51 +02:00
|
|
|
import org.bukkit.entity.*;
|
2018-11-06 04:33:10 +01:00
|
|
|
import org.bukkit.scheduler.BukkitRunnable;
|
|
|
|
|
2019-08-01 19:47:20 +02:00
|
|
|
import java.util.*;
|
2019-07-19 02:49:41 +02:00
|
|
|
import java.util.stream.Collectors;
|
2018-11-06 04:33:10 +01:00
|
|
|
|
|
|
|
public class StackingTask extends BukkitRunnable {
|
|
|
|
|
2019-07-31 06:29:10 +02:00
|
|
|
private final UltimateStacker plugin;
|
2018-11-06 04:33:10 +01:00
|
|
|
|
2019-06-27 20:34:33 +02:00
|
|
|
private EntityStackManager stackManager;
|
|
|
|
|
2019-09-03 22:38:00 +02:00
|
|
|
ConfigurationSection configurationSection = UltimateStacker.getInstance().getMobFile();
|
2019-07-19 02:49:41 +02:00
|
|
|
|
|
|
|
private List<UUID> processed = new ArrayList<>();
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-31 06:29:10 +02:00
|
|
|
private int maxEntityStackSize = Setting.MAX_STACK_ENTITIES.getInt();
|
|
|
|
private int minEntityStackSize = Setting.MIN_STACK_ENTITIES.getInt();
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-08-08 03:57:53 +02:00
|
|
|
private int maxPerTypeStacksPerChunk = Setting.MAX_PER_TYPE_STACKS_PER_CHUNK.getInt();
|
2019-08-07 20:24:49 +02:00
|
|
|
|
2019-07-31 06:29:10 +02:00
|
|
|
public StackingTask(UltimateStacker plugin) {
|
|
|
|
this.plugin = plugin;
|
|
|
|
this.stackManager = plugin.getEntityStackManager();
|
2019-03-21 04:36:58 +01:00
|
|
|
|
|
|
|
// Start stacking task.
|
2019-08-08 15:01:40 +02:00
|
|
|
runTaskTimer(plugin, 0, Setting.STACK_SEARCH_TICK_SPEED.getInt());
|
2018-11-06 04:33:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2019-07-19 02:49:41 +02:00
|
|
|
// Should entities be stacked?
|
|
|
|
if (!Setting.STACK_ENTITIES.getBoolean()) return;
|
|
|
|
|
|
|
|
// Loop through each world.
|
2018-11-06 04:33:10 +01:00
|
|
|
for (World world : Bukkit.getWorlds()) {
|
2019-07-19 02:49:41 +02:00
|
|
|
// If world is disabled then continue to the next world.
|
2019-07-31 21:47:01 +02:00
|
|
|
if (isWorldDisabled(world)) continue;
|
2018-11-06 04:33:10 +01:00
|
|
|
|
2019-07-19 04:19:33 +02:00
|
|
|
// Get the loaded entities from the current world and reverse them.
|
|
|
|
List<Entity> entities = new ArrayList<>(world.getEntities());
|
2019-01-15 05:45:25 +01:00
|
|
|
Collections.reverse(entities);
|
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Loop through the entities.
|
|
|
|
for (Entity entity : entities) {
|
2019-07-31 21:02:05 +02:00
|
|
|
// Get entity location to pass around as its faster this way.
|
|
|
|
Location location = entity.getLocation();
|
2019-07-19 04:19:33 +02:00
|
|
|
// Check to see if entity is stackable.
|
2019-07-31 21:02:05 +02:00
|
|
|
if (!isEntityStackable(entity, location)) continue;
|
2019-07-19 02:49:41 +02:00
|
|
|
// Make sure our entity has not already been processed.
|
|
|
|
// Skip it if it has been.
|
|
|
|
if (this.processed.contains(entity.getUniqueId())) continue;
|
2019-06-23 00:28:47 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Cast our entity to living entity.
|
|
|
|
LivingEntity livingEntity = (LivingEntity) entity;
|
2018-11-08 04:26:38 +01:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Process the entity.
|
2019-07-31 21:02:05 +02:00
|
|
|
this.processEntity(livingEntity, location);
|
2018-11-06 04:33:10 +01:00
|
|
|
|
2019-06-27 20:34:33 +02:00
|
|
|
}
|
|
|
|
}
|
2019-08-01 19:47:20 +02:00
|
|
|
// Clear caches in preparation for the next run.
|
|
|
|
this.processed.clear();
|
|
|
|
plugin.getEntityUtils().clearChunkCache();
|
2019-06-27 20:34:33 +02:00
|
|
|
}
|
2018-11-06 04:33:10 +01:00
|
|
|
|
2019-07-31 21:47:01 +02:00
|
|
|
public boolean isWorldDisabled(World world) {
|
|
|
|
List<String> disabledWorlds = Setting.DISABLED_WORLDS.getStringList();
|
|
|
|
return disabledWorlds.stream().anyMatch(worldStr -> world.getName().equalsIgnoreCase(worldStr));
|
|
|
|
}
|
|
|
|
|
2019-07-31 21:02:05 +02:00
|
|
|
private boolean isEntityStackable(Entity entity, Location location) {
|
2019-07-19 02:49:41 +02:00
|
|
|
// Make sure we have the correct entity type and that it is valid.
|
|
|
|
if (!entity.isValid()
|
|
|
|
|| !(entity instanceof LivingEntity)
|
|
|
|
|| entity instanceof HumanEntity
|
|
|
|
|| entity instanceof ArmorStand
|
2019-06-28 05:14:40 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Make sure the entity is not in love.
|
|
|
|
|| entity.hasMetadata("inLove")
|
|
|
|
// Or in breeding cooldown.
|
2019-08-19 03:48:35 +02:00
|
|
|
|| entity.hasMetadata("breedCooldown"))
|
|
|
|
return false;
|
2019-07-19 02:49:41 +02:00
|
|
|
|
2019-08-19 03:48:35 +02:00
|
|
|
// Allow spawn if stackreasons are set and match, or if from a spawner
|
|
|
|
final String spawnReason = entity.hasMetadata("US_REASON") ? entity.getMetadata("US_REASON").get(0).asString() : null;
|
|
|
|
List<String> stackReasons;
|
|
|
|
if (Setting.ONLY_STACK_FROM_SPAWNERS.getBoolean()) {
|
|
|
|
// If only stack from spawners is enabled, make sure the entity spawned from a spawner.
|
|
|
|
if (!"SPAWNER".equals(spawnReason))
|
|
|
|
return false;
|
|
|
|
} else if (!(stackReasons = Setting.STACK_REASONS.getStringList()).isEmpty() && !stackReasons.contains(spawnReason))
|
|
|
|
// Only stack if on the list of events to stack
|
2019-06-28 05:14:40 +02:00
|
|
|
return false;
|
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Cast our entity to living entity.
|
|
|
|
LivingEntity livingEntity = (LivingEntity) entity;
|
2019-06-15 11:05:53 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// If only stack on surface is enabled make sure the entity is on a surface then entity is stackable.
|
|
|
|
return !Setting.ONLY_STACK_ON_SURFACE.getBoolean()
|
|
|
|
|| Methods.canFly(livingEntity)
|
2019-08-09 02:52:41 +02:00
|
|
|
|| entity.getType().name().equals("SHULKER")
|
2019-07-31 21:02:05 +02:00
|
|
|
|| (livingEntity.isOnGround() || location.getBlock().isLiquid());
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
}
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-31 21:02:05 +02:00
|
|
|
private void processEntity(LivingEntity livingEntity, Location location) {
|
2019-07-19 02:49:41 +02:00
|
|
|
// Get the stack from the entity. It should be noted that this value will
|
|
|
|
// be null if our entity is not a stack.
|
2019-07-31 06:29:10 +02:00
|
|
|
EntityStack stack = plugin.getEntityStackManager().getStack(livingEntity);
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Is this entity stacked?
|
|
|
|
boolean isStack = stack != null;
|
2018-11-06 04:33:10 +01:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// The amount that is stackable.
|
|
|
|
int amountToStack = isStack ? stack.getAmount() : 1;
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Attempt to split our stack. If the split is successful then skip this entity.
|
|
|
|
if (isStack && attemptSplit(stack, livingEntity)) return;
|
|
|
|
|
|
|
|
// If this entity is named or disabled then skip it.
|
|
|
|
if (!isStack && livingEntity.getCustomName() != null
|
|
|
|
|| !configurationSection.getBoolean("Mobs." + livingEntity.getType().name() + ".Enabled"))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Get the maximum stack size for this entity.
|
|
|
|
int maxEntityStackSize = getEntityStackSize(livingEntity);
|
|
|
|
|
|
|
|
// Get similar entities around our entity and make sure those entities are both compatible and stackable.
|
2019-07-31 21:02:05 +02:00
|
|
|
List<LivingEntity> stackableFriends = plugin.getEntityUtils().getSimilarEntitiesAroundEntity(livingEntity, location)
|
2019-08-01 19:47:20 +02:00
|
|
|
.stream().filter(entity -> isEntityStackable(entity, location))
|
|
|
|
.collect(Collectors.toCollection(LinkedList::new));
|
2019-07-19 02:49:41 +02:00
|
|
|
|
|
|
|
// Loop through our similar stackable entities.
|
|
|
|
for (LivingEntity entity : stackableFriends) {
|
|
|
|
// Make sure the entity has not already been processed.
|
|
|
|
if (this.processed.contains(entity.getUniqueId())) continue;
|
|
|
|
|
|
|
|
// Get this entities friendStack.
|
|
|
|
EntityStack friendStack = stackManager.getStack(entity);
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Check to see if this entity is stacked and friendStack plus
|
|
|
|
// our amount to stack is not above our max friendStack size
|
|
|
|
// for this entity.
|
|
|
|
if (friendStack != null && (friendStack.getAmount() + amountToStack) <= maxEntityStackSize) {
|
2019-06-30 01:32:16 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Add one to the found friendStack.
|
|
|
|
friendStack.addAmount(amountToStack);
|
|
|
|
|
|
|
|
// Add our entities health to the friendStacks health.
|
|
|
|
friendStack.addHealth(entity.getHealth());
|
|
|
|
|
|
|
|
// Fix the friendStacks health.
|
|
|
|
if (!isStack)
|
|
|
|
friendStack.addHealth(entity.getHealth());
|
2019-06-30 01:32:16 +02:00
|
|
|
else
|
2019-07-19 02:49:41 +02:00
|
|
|
friendStack.mergeHealth(stack);
|
2019-06-30 01:32:16 +02:00
|
|
|
|
2019-06-28 22:00:30 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
fixHealth(entity, livingEntity);
|
2019-06-30 02:16:01 +02:00
|
|
|
if (Setting.STACK_ENTITY_HEALTH.getBoolean())
|
2019-07-19 02:49:41 +02:00
|
|
|
entity.setHealth(entity.getMaxHealth() < livingEntity.getHealth()
|
|
|
|
? entity.getMaxHealth() : livingEntity.getHealth());
|
|
|
|
|
2019-06-28 22:00:30 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Remove our entity and mark it as processed.
|
|
|
|
livingEntity.remove();
|
|
|
|
processed.add(livingEntity.getUniqueId());
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
return;
|
|
|
|
} else if (friendStack == null
|
|
|
|
&& isStack
|
|
|
|
&& (stack.getAmount() + 1) <= maxEntityStackSize
|
2019-06-27 20:34:33 +02:00
|
|
|
&& Methods.canFly(entity)
|
|
|
|
&& Setting.ONLY_STACK_FLYING_DOWN.getBoolean()
|
2019-07-31 21:02:05 +02:00
|
|
|
&& location.getY() > entity.getLocation().getY()) {
|
2019-06-30 01:32:16 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Create a new stack with the current stacks amount and add one to it.
|
|
|
|
EntityStack newStack = stackManager.addStack(entity, stack.getAmount() + 1);
|
2019-06-28 22:00:30 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Fix the entities health.
|
|
|
|
newStack.mergeHealth(stack);
|
|
|
|
newStack.addHealth(livingEntity.getHealth());
|
|
|
|
fixHealth(livingEntity, entity);
|
2019-06-30 02:16:01 +02:00
|
|
|
if (Setting.STACK_ENTITY_HEALTH.getBoolean())
|
|
|
|
entity.setHealth(entity.getHealth());
|
2019-06-28 22:00:30 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Remove our entities stack from the stack manager.
|
|
|
|
stackManager.removeStack(livingEntity);
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Remove our entity and mark it as processed.
|
|
|
|
livingEntity.remove();
|
|
|
|
processed.add(livingEntity.getUniqueId());
|
|
|
|
|
|
|
|
return;
|
2018-11-06 04:33:10 +01:00
|
|
|
}
|
|
|
|
}
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// If our entity is stacked then skip this entity.
|
|
|
|
if (isStack) return;
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Remove all stacked entities from our stackable friends.
|
|
|
|
stackableFriends.removeIf(stackManager::isStacked);
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-08-08 14:36:48 +02:00
|
|
|
// If the stack cap is met then delete this entity.
|
|
|
|
if (maxPerTypeStacksPerChunk != -1
|
|
|
|
&& (plugin.getEntityUtils().getSimilarStacksInChunk(livingEntity) + 1) > maxPerTypeStacksPerChunk) {
|
|
|
|
livingEntity.remove();
|
|
|
|
this.processed.add(livingEntity.getUniqueId());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// If there are none or not enough stackable friends left to create a new entity,
|
|
|
|
// the stack sizes overlap then skip this entity.
|
|
|
|
if (stackableFriends.isEmpty()
|
|
|
|
|| stackableFriends.size() < minEntityStackSize - 1
|
|
|
|
|| minEntityStackSize > maxEntityStackSize) return;
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// If a stack was never found create a new one.
|
2019-07-30 04:00:14 +02:00
|
|
|
EntityStack newStack = stackManager.addStack(new EntityStack(livingEntity,
|
|
|
|
Math.min((stackableFriends.size() + 1), maxEntityStackSize)));
|
2019-06-27 20:34:33 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Loop through the unstacked and unprocessed stackable friends while not creating
|
|
|
|
// a stack larger than the maximum.
|
|
|
|
stackableFriends.stream().filter(entity -> !stackManager.isStacked(entity)
|
|
|
|
&& !this.processed.contains(entity.getUniqueId())).limit(maxEntityStackSize).forEach(entity -> {
|
2019-06-28 22:00:30 +02:00
|
|
|
|
2019-07-30 04:00:14 +02:00
|
|
|
// Make sure we're not naming some poor kids pet.
|
|
|
|
if (entity.getCustomName() != null) {
|
|
|
|
processed.add(livingEntity.getUniqueId());
|
2019-07-31 20:26:47 +02:00
|
|
|
newStack.addAmount(-1);
|
2019-07-30 04:00:14 +02:00
|
|
|
return;
|
|
|
|
}
|
2019-07-19 02:49:41 +02:00
|
|
|
// Fix the entities health.
|
|
|
|
fixHealth(livingEntity, entity);
|
|
|
|
newStack.addHealth(entity.getHealth());
|
2019-06-28 22:00:30 +02:00
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Remove our entity and mark it as processed.
|
2019-06-27 20:34:33 +02:00
|
|
|
entity.remove();
|
2019-07-19 02:49:41 +02:00
|
|
|
processed.add(entity.getUniqueId());
|
2019-06-27 20:34:33 +02:00
|
|
|
});
|
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
// Update our stacks health.
|
|
|
|
updateHealth(newStack);
|
|
|
|
|
|
|
|
// Update our stack.
|
|
|
|
newStack.updateStack();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void updateHealth(EntityStack stack) {
|
|
|
|
if (Setting.STACK_ENTITY_HEALTH.getBoolean())
|
|
|
|
stack.updateHealth(stack.getEntity());
|
2018-11-06 04:33:10 +01:00
|
|
|
}
|
2019-06-23 02:10:22 +02:00
|
|
|
|
2019-07-19 02:50:07 +02:00
|
|
|
|
|
|
|
public boolean attemptSplit(EntityStack stack, LivingEntity livingEntity) {
|
2019-07-17 14:56:50 +02:00
|
|
|
int stackSize = stack.getAmount();
|
2019-07-19 02:50:07 +02:00
|
|
|
int maxEntityStackAmount = getEntityStackSize(livingEntity);
|
2019-07-17 14:56:50 +02:00
|
|
|
|
2019-07-19 02:50:07 +02:00
|
|
|
if (stackSize <= maxEntityStackAmount) return false;
|
2019-07-19 02:50:29 +02:00
|
|
|
|
2019-07-19 02:50:07 +02:00
|
|
|
for (int i = stackSize; i > 0; i -= maxEntityStackAmount)
|
2019-07-31 06:29:10 +02:00
|
|
|
this.processed.add(plugin.getEntityStackManager()
|
|
|
|
.addStack(plugin.getEntityUtils().newEntity(livingEntity), Math.min(i, maxEntityStackAmount)).getEntityUniqueId());
|
2019-07-17 14:56:50 +02:00
|
|
|
|
2019-07-19 02:50:07 +02:00
|
|
|
// Remove our entities stack from the stack manager.
|
|
|
|
stackManager.removeStack(livingEntity);
|
|
|
|
|
|
|
|
// Remove our entity and mark it as processed.
|
|
|
|
livingEntity.remove();
|
|
|
|
processed.add(livingEntity.getUniqueId());
|
|
|
|
return true;
|
2019-07-17 14:56:50 +02:00
|
|
|
}
|
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
|
2019-06-28 22:00:30 +02:00
|
|
|
private void fixHealth(LivingEntity entity, LivingEntity initialEntity) {
|
2019-06-30 01:32:16 +02:00
|
|
|
if (!Setting.STACK_ENTITY_HEALTH.getBoolean() && Setting.CARRY_OVER_LOWEST_HEALTH.getBoolean() && initialEntity.getHealth() < entity.getHealth())
|
2019-06-28 22:00:30 +02:00
|
|
|
entity.setHealth(initialEntity.getHealth());
|
|
|
|
}
|
|
|
|
|
2019-07-19 02:49:41 +02:00
|
|
|
private int getEntityStackSize(LivingEntity initialEntity) {
|
|
|
|
if (configurationSection.getInt("Mobs." + initialEntity.getType().name() + ".Max Stack Size") != -1)
|
|
|
|
maxEntityStackSize = configurationSection.getInt("Mobs." + initialEntity.getType().name() + ".Max Stack Size");
|
|
|
|
return maxEntityStackSize;
|
2019-06-30 01:32:16 +02:00
|
|
|
}
|
2018-11-06 04:33:10 +01:00
|
|
|
}
|