diff --git a/pom.xml b/pom.xml index aeba90f..dff2ba4 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ com.songoda UltimateStacker 4.0.0 - 2.3.0 + 2.3.1 clean install UltimateStacker-${project.version} @@ -112,7 +112,7 @@ com.songoda SongodaCore - 2.6.16 + 2.6.17-SNAPSHOT compile diff --git a/src/main/java/com/songoda/ultimatestacker/UltimateStacker.java b/src/main/java/com/songoda/ultimatestacker/UltimateStacker.java index b8fdd84..e23b20d 100644 --- a/src/main/java/com/songoda/ultimatestacker/UltimateStacker.java +++ b/src/main/java/com/songoda/ultimatestacker/UltimateStacker.java @@ -49,6 +49,7 @@ import com.songoda.ultimatestacker.tasks.StackingTask; import com.songoda.ultimatestacker.utils.Methods; import org.apache.commons.lang.WordUtils; import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.entity.EntityType; import org.bukkit.entity.Item; @@ -382,6 +383,19 @@ public class UltimateStacker extends SongodaPlugin { //////// Convenient API ////////// + /** + * Spawn a stacked item at a location + * + * @param item The item to spawn + * @param amount The amount of items to spawn + * @param location The location to spawn the item + */ + public static void spawnStackedItem(ItemStack item, int amount, Location location) { + location.getWorld().dropItem(location, item, dropped -> { + updateItemAmount(dropped, amount); + }); + } + /** * Change the stacked amount for this item * diff --git a/src/main/java/com/songoda/ultimatestacker/listeners/DeathListeners.java b/src/main/java/com/songoda/ultimatestacker/listeners/DeathListeners.java index 0232f20..620d073 100644 --- a/src/main/java/com/songoda/ultimatestacker/listeners/DeathListeners.java +++ b/src/main/java/com/songoda/ultimatestacker/listeners/DeathListeners.java @@ -7,6 +7,7 @@ import com.songoda.core.lootables.loot.DropUtils; import com.songoda.ultimatestacker.UltimateStacker; import com.songoda.ultimatestacker.settings.Settings; import com.songoda.ultimatestacker.stackable.entity.EntityStack; +import org.bukkit.GameMode; import org.bukkit.GameRule; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; @@ -153,8 +154,9 @@ public class DeathListeners implements Listener { if (!plugin.getEntityStackManager().isStackedAndLoaded(entity)) return; EntityStack stack = plugin.getEntityStackManager().getStack(entity); - if (Settings.KILL_WHOLE_STACK_ON_DEATH.getBoolean() && Settings.REALISTIC_DAMAGE.getBoolean()) { - Player player = (Player) event.getDamager(); + Player player = (Player) event.getDamager(); + + if (Settings.KILL_WHOLE_STACK_ON_DEATH.getBoolean() && Settings.REALISTIC_DAMAGE.getBoolean() && !player.getGameMode().equals(GameMode.CREATIVE)) { ItemStack tool = player.getInventory().getItemInHand(); if (tool.getType().getMaxDurability() < 1 || (tool.getItemMeta() != null && (tool.getItemMeta().isUnbreakable() || (ServerProject.isServer(ServerProject.SPIGOT, ServerProject.PAPER) && tool.getItemMeta().isUnbreakable())))) diff --git a/src/main/java/com/songoda/ultimatestacker/tasks/StackingTask.java b/src/main/java/com/songoda/ultimatestacker/tasks/StackingTask.java index 97ae05d..a0950a1 100644 --- a/src/main/java/com/songoda/ultimatestacker/tasks/StackingTask.java +++ b/src/main/java/com/songoda/ultimatestacker/tasks/StackingTask.java @@ -17,7 +17,32 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.*; +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.inventory.EntityEquipment; import org.bukkit.scheduler.BukkitRunnable; @@ -30,6 +55,9 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; public class StackingTask extends BukkitRunnable { @@ -42,7 +70,7 @@ public class StackingTask extends BukkitRunnable { private final Map cachedChunks = new HashMap<>(); - private final HashMap entityStackSizes = new HashMap(); + private final Map entityStackSizes = new HashMap<>(); private final int maxEntityStackSize = Settings.MAX_STACK_ENTITIES.getInt(), minEntityStackSize = Settings.MIN_STACK_ENTITIES.getInt(), searchRadius = Settings.SEARCH_RADIUS.getInt(), @@ -83,11 +111,10 @@ public class StackingTask extends BukkitRunnable { // Get the loaded entities from the current world and reverse them. List entities; try { - entities = sWorld.getLivingEntities(); - } catch (Exception ignored) { + entities = getLivingEntitiesSync(sWorld).get(); + } catch (ExecutionException | InterruptedException ex) { + ex.printStackTrace(); continue; - // Sometimes accessing this method asynchronously throws an error. This is super rare and - // as such doesn't really affect the plugin so we're just going to ignore this failure. } Collections.reverse(entities); @@ -113,6 +140,20 @@ public class StackingTask extends BukkitRunnable { this.cachedChunks.clear(); } + 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().scheduleSyncDelayedTask(this.plugin, () -> future.complete(cachedChunk.getEntities())); + + return future; + } + public boolean isWorldDisabled(World world) { return disabledWorlds.stream().anyMatch(worldStr -> world.getName().equalsIgnoreCase(worldStr)); } @@ -243,6 +284,10 @@ public class StackingTask extends BukkitRunnable { // Make the friend the new stack host. EntityStack newStack = stackManager.updateStack(livingEntity, entity); + if (newStack == null) { + continue; + } + // Add our entity to that stack plugin.getDataManager().createStackedEntity(newStack, newStack.addEntityToStack(livingEntity)); @@ -367,19 +412,21 @@ public class StackingTask extends BukkitRunnable { List entities = new ArrayList<>(); for (CachedChunk chunk : getNearbyChunks(sWorld, location, radius, singleChunk)) { if (chunk == null) continue; - Entity[] entityArray = new Entity[0]; + Entity[] entityArray; if (cachedChunks.containsKey(chunk)) { entityArray = cachedChunks.get(chunk); } else { try { - entityArray = chunk.getEntities(); + entityArray = getEntitiesInChunkSync(chunk).get(); cachedChunks.put(chunk, entityArray); - } catch (Exception ignored) { - // Sometimes accessing this method asynchronously throws an error. This is super rare and - // as such doesn't really affect the plugin so we're just going to ignore this failure. + } catch (ExecutionException | InterruptedException ex) { + ex.printStackTrace(); + continue; } } + if (entityArray == null) continue; + for (Entity e : entityArray) { if (e == null) continue; if (e.getWorld() != location.getWorld() @@ -388,6 +435,7 @@ public class StackingTask extends BukkitRunnable { entities.add((LivingEntity) e); } } + return entities; }