UltimateStacker/src/main/java/com/songoda/ultimatestacker/tasks/StackingTask.java

292 lines
12 KiB
Java
Raw Normal View History

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;
import com.songoda.ultimatestacker.utils.Methods;
import com.songoda.ultimatestacker.utils.settings.Setting;
2018-11-06 04:33:10 +01:00
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
private EntityStackManager stackManager;
2019-07-19 02:49:41 +02:00
ConfigurationSection configurationSection = UltimateStacker.getInstance().getMobFile().getConfig();
private List<UUID> processed = new ArrayList<>();
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-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.
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-07-19 02:49:41 +02:00
// Cast our entity to living entity.
LivingEntity livingEntity = (LivingEntity) entity;
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-08-01 19:47:20 +02:00
// Clear caches in preparation for the next run.
this.processed.clear();
plugin.getEntityUtils().clearChunkCache();
}
2018-11-06 04:33:10 +01: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.
|| entity.hasMetadata("breedCooldown")
// If only stack from spawners is enabled make sure the entity spawned from a spawner.
|| Setting.ONLY_STACK_FROM_SPAWNERS.getBoolean()
&& !(entity.hasMetadata("US_REASON")
&& entity.getMetadata("US_REASON").get(0).asString().equals("SPAWNER")))
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-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-07-19 02:49:41 +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-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-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-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-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());
else
2019-07-19 02:49:41 +02:00
friendStack.mergeHealth(stack);
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-07-19 02:49:41 +02:00
// Remove our entity and mark it as processed.
livingEntity.remove();
processed.add(livingEntity.getUniqueId());
2019-07-19 02:49:41 +02:00
return;
} else if (friendStack == null
&& isStack
&& (stack.getAmount() + 1) <= maxEntityStackSize
&& Methods.canFly(entity)
&& Setting.ONLY_STACK_FLYING_DOWN.getBoolean()
2019-07-31 21:02:05 +02:00
&& location.getY() > entity.getLocation().getY()) {
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-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-07-19 02:49:41 +02:00
// Remove our entities stack from the stack manager.
stackManager.removeStack(livingEntity);
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-07-19 02:49:41 +02:00
// If our entity is stacked then skip this entity.
if (isStack) return;
2019-07-19 02:49:41 +02:00
// Remove all stacked entities from our stackable friends.
stackableFriends.removeIf(stackManager::isStacked);
// 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-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-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-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-07-19 02:49:41 +02:00
// Remove our entity and mark it as processed.
entity.remove();
2019-07-19 02:49:41 +02:00
processed.add(entity.getUniqueId());
});
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
public boolean attemptSplit(EntityStack stack, LivingEntity livingEntity) {
int stackSize = stack.getAmount();
int maxEntityStackAmount = getEntityStackSize(livingEntity);
if (stackSize <= maxEntityStackAmount) return false;
2019-07-19 02:50:29 +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());
// 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-19 02:49:41 +02:00
private void fixHealth(LivingEntity entity, LivingEntity initialEntity) {
if (!Setting.STACK_ENTITY_HEALTH.getBoolean() && Setting.CARRY_OVER_LOWEST_HEALTH.getBoolean() && initialEntity.getHealth() < entity.getHealth())
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;
}
2018-11-06 04:33:10 +01:00
}