diff --git a/src/main/java/com/songoda/ultimatestacker/UltimateStacker.java b/src/main/java/com/songoda/ultimatestacker/UltimateStacker.java index cecfa93..5c81465 100644 --- a/src/main/java/com/songoda/ultimatestacker/UltimateStacker.java +++ b/src/main/java/com/songoda/ultimatestacker/UltimateStacker.java @@ -26,8 +26,7 @@ import com.songoda.ultimatestacker.database.DataManager; import com.songoda.ultimatestacker.database.migrations._1_InitialMigration; import com.songoda.ultimatestacker.database.migrations._2_EntityStacks; import com.songoda.ultimatestacker.database.migrations._3_BlockStacks; -import com.songoda.ultimatestacker.database.migrations._4_DataPurge; -import com.songoda.ultimatestacker.database.migrations._5_StackedEntitiesTableUpdate; +import com.songoda.ultimatestacker.database.migrations._6_RemoveStackedEntityTable; import com.songoda.ultimatestacker.hook.StackerHook; import com.songoda.ultimatestacker.hook.hooks.JobsHook; import com.songoda.ultimatestacker.listeners.*; @@ -49,6 +48,7 @@ import com.songoda.ultimatestacker.stackable.spawner.SpawnerStackManager; import com.songoda.ultimatestacker.tasks.StackingTask; import com.songoda.ultimatestacker.utils.Async; import com.songoda.ultimatestacker.utils.Methods; +import io.lumine.mythic.bukkit.listeners.ChunkListeners; import org.apache.commons.lang.WordUtils; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -106,7 +106,7 @@ public class UltimateStacker extends SongodaPlugin { @Override public void onPluginDisable() { - this.stackingTask.cancel(); + this.stackingTask.stop(); this.stackingTask = null; this.dataManager.bulkUpdateSpawners(this.spawnerStackManager.getStacks()); HologramManager.removeAllHolograms(); @@ -235,8 +235,7 @@ public class UltimateStacker extends SongodaPlugin { new _1_InitialMigration(), new _2_EntityStacks(), new _3_BlockStacks(), - new _4_DataPurge(), - new _5_StackedEntitiesTableUpdate()); + new _6_RemoveStackedEntityTable()); this.dataMigrationManager.runMigrations(); } @@ -260,12 +259,8 @@ public class UltimateStacker extends SongodaPlugin { } } }); - this.dataManager.getEntities((entities) -> { - entityStackManager.addStacks(entities.values()); - entityStackManager.tryAndLoadColdEntities(); - this.stackingTask = new StackingTask(this); - getServer().getPluginManager().registerEvents(new ChunkListeners(entityStackManager), this); - }); + + this.stackingTask = new StackingTask(this); final boolean useBlockHolo = Settings.BLOCK_HOLOGRAMS.getBoolean(); this.dataManager.getBlocks((blocks) -> { this.blockStackManager.addBlocks(blocks); @@ -302,7 +297,7 @@ public class UltimateStacker extends SongodaPlugin { this.setLocale(getConfig().getString("System.Language Mode"), true); this.locale.reloadMessages(); - this.stackingTask.cancel(); + this.stackingTask.stop(); this.stackingTask = new StackingTask(this); this.mobFile.load(); diff --git a/src/main/java/com/songoda/ultimatestacker/commands/CommandRemoveAll.java b/src/main/java/com/songoda/ultimatestacker/commands/CommandRemoveAll.java index 01d36c8..840a8eb 100644 --- a/src/main/java/com/songoda/ultimatestacker/commands/CommandRemoveAll.java +++ b/src/main/java/com/songoda/ultimatestacker/commands/CommandRemoveAll.java @@ -46,10 +46,9 @@ public class CommandRemoveAll extends AbstractCommand { for (Entity entityO : world.getEntities()) { if (entityO instanceof Player) continue; - if (entityO instanceof LivingEntity && (stackManager.isStackedAndLoaded((LivingEntity)entityO) || all) + if (entityO instanceof LivingEntity && (stackManager.isStackedEntity(entityO) || all) && type.equalsIgnoreCase("entities")) { - entityO.remove(); - plugin.getEntityStackManager().removeStack(entityO); + plugin.getEntityStackManager().getStack((LivingEntity) entityO).destroy(); amountRemoved++; } else if (entityO.getType() == EntityType.DROPPED_ITEM && type.equalsIgnoreCase("items")) { if (!UltimateStacker.hasCustomAmount((Item)entityO) && !all) diff --git a/src/main/java/com/songoda/ultimatestacker/commands/CommandSpawn.java b/src/main/java/com/songoda/ultimatestacker/commands/CommandSpawn.java index 085f499..c5b0e8f 100644 --- a/src/main/java/com/songoda/ultimatestacker/commands/CommandSpawn.java +++ b/src/main/java/com/songoda/ultimatestacker/commands/CommandSpawn.java @@ -56,9 +56,7 @@ public class CommandSpawn extends AbstractCommand { sender.sendMessage(TextUtils.formatText("&6" + list)); } else { LivingEntity entity = (LivingEntity)player.getWorld().spawnEntity(player.getLocation(), type); - EntityStack stack = plugin.getEntityStackManager().addStack(entity); - stack.createDuplicates(((Methods.isInt(args[1])) ? Integer.parseInt(args[1]) : 1) - 1); - stack.updateStack(); + EntityStack stack = plugin.getEntityStackManager().createStack(entity, Integer.parseInt(args[1])); plugin.getStackingTask().attemptSplit(stack, entity); } diff --git a/src/main/java/com/songoda/ultimatestacker/convert/StackMobConvert.java b/src/main/java/com/songoda/ultimatestacker/convert/StackMobConvert.java index 72be5db..96eb27a 100644 --- a/src/main/java/com/songoda/ultimatestacker/convert/StackMobConvert.java +++ b/src/main/java/com/songoda/ultimatestacker/convert/StackMobConvert.java @@ -36,14 +36,6 @@ public class StackMobConvert implements Convert { @Override public void convertEntities() { - EntityStackManager entityStackManager = plugin.getEntityStackManager(); - for (Map.Entry entry : stackMob.getStorageManager().getAmountCache().entrySet()) { - if (!entityStackManager.isStackedAndLoaded(entry.getKey())) { - entityStackManager.addLegacyColdStack(entry.getKey(), entry.getValue()); - continue; - } - } - } @Override diff --git a/src/main/java/com/songoda/ultimatestacker/convert/WildStackerConvert.java b/src/main/java/com/songoda/ultimatestacker/convert/WildStackerConvert.java index f943450..be50053 100644 --- a/src/main/java/com/songoda/ultimatestacker/convert/WildStackerConvert.java +++ b/src/main/java/com/songoda/ultimatestacker/convert/WildStackerConvert.java @@ -43,23 +43,6 @@ public class WildStackerConvert implements Convert { @Override public void convertEntities() { - EntityStackManager entityStackManager = plugin.getEntityStackManager(); - - DatabaseConnector connector = new SQLiteConnector(this.wildStacker); - connector.connect(connection -> { - - try (Statement statement = connection.createStatement()) { - ResultSet result = statement.executeQuery("SELECT uuid, stackAmount FROM entities"); - while (result.next()) { - UUID uuid = UUID.fromString(result.getString("uuid")); - int amount = result.getInt("stackAmount"); - if (!entityStackManager.isEntityInColdStorage(uuid)) - entityStackManager.addLegacyColdStack(uuid, amount); - } - } - }); - - } @Override diff --git a/src/main/java/com/songoda/ultimatestacker/database/DataManager.java b/src/main/java/com/songoda/ultimatestacker/database/DataManager.java index c3fe296..9ad0357 100644 --- a/src/main/java/com/songoda/ultimatestacker/database/DataManager.java +++ b/src/main/java/com/songoda/ultimatestacker/database/DataManager.java @@ -6,7 +6,6 @@ import com.songoda.core.database.DatabaseConnector; import com.songoda.core.database.DatabaseType; import com.songoda.ultimatestacker.settings.Settings; import com.songoda.ultimatestacker.stackable.block.BlockStack; -import com.songoda.ultimatestacker.stackable.entity.ColdEntityStack; import com.songoda.ultimatestacker.stackable.entity.EntityStack; import com.songoda.ultimatestacker.stackable.entity.StackedEntity; import com.songoda.ultimatestacker.stackable.spawner.SpawnerStack; @@ -122,130 +121,6 @@ public class DataManager extends DataManagerAbstract { }); } - public void createHostEntity(ColdEntityStack stack) { - this.runAsync(() -> { - try (Connection connection = this.databaseConnector.getConnection()) { - String createSerializedEntity = "INSERT " + getSyntax("INTO ", DatabaseType.MYSQL) + getSyntax("OR REPLACE INTO ", DatabaseType.SQLITE) + this.getTablePrefix() + "host_entities (uuid, create_duplicates) VALUES (?, ?)"; - PreparedStatement statement = connection.prepareStatement(createSerializedEntity); - if (stack == null || stack.getHostUniqueId() == null) return; - statement.setString(1, stack.getHostUniqueId().toString()); - statement.setInt(2, stack.getCreateDuplicates()); - statement.executeUpdate(); - int stackId = this.lastInsertedId(connection, "host_entities"); - this.sync(() -> stack.setId(stackId)); - } catch (Exception ex) { - ex.printStackTrace(); - } - }); - } - - public void createStackedEntity(EntityStack hostStack, StackedEntity stackedEntity) { - this.runAsync(() -> { - try (Connection connection = this.databaseConnector.getConnection()){ - String createSerializedEntity = "INSERT " + getSyntax("INTO ", DatabaseType.MYSQL) + getSyntax("OR REPLACE INTO ", DatabaseType.SQLITE) + this.getTablePrefix() + "stacked_entities (uuid, host, serialized_entity) VALUES (?, ?, ?) " - + (Settings.MYSQL_ENABLED.getBoolean() ? "ON DUPLICATE KEY UPDATE host = ?, serialized_entity = ?" : "ON CONFLICT(uuid) DO UPDATE SET host = ?, serialized_entity = ?"); - PreparedStatement statement = connection.prepareStatement(createSerializedEntity); - if (hostStack.getHostUniqueId() == null) return; - statement.setString(1, stackedEntity.getUniqueId().toString()); - statement.setInt(2, hostStack.getId()); - statement.setBytes(3, stackedEntity.getSerializedEntity()); - statement.setInt(4, hostStack.getId()); - statement.setBytes(5, stackedEntity.getSerializedEntity()); - statement.executeUpdate(); - } catch (Exception ex) { - ex.printStackTrace(); - } - }); - } - - public void createStackedEntities(ColdEntityStack hostStack, List stackedEntities) { - this.runAsync(() -> { - try (Connection connection = this.databaseConnector.getConnection()) { - String createSerializedEntity = "REPLACE INTO " + this.getTablePrefix() + "stacked_entities (uuid, host, serialized_entity) VALUES (?, ?, ?)"; - PreparedStatement statement = connection.prepareStatement(createSerializedEntity); - if (hostStack.getHostUniqueId() == null) return; - for (StackedEntity entity : stackedEntities) { - statement.setString(1, entity.getUniqueId().toString()); - statement.setInt(2, hostStack.getId()); - statement.setBytes(3, entity.getSerializedEntity()); - statement.addBatch(); - } - statement.executeBatch(); - } catch (Exception ex) { - ex.printStackTrace(); - } - }); - } - - public void updateHost(ColdEntityStack hostStack) { - this.runAsync(() -> { - try (Connection connection = this.databaseConnector.getConnection()) { - String updateHost = "UPDATE " + this.getTablePrefix() + "host_entities SET uuid = ?, create_duplicates = ?, updated_at = current_timestamp WHERE id = ?"; - PreparedStatement statement = connection.prepareStatement(updateHost); - if (hostStack.getHostUniqueId() == null) return; - statement.setString(1, hostStack.getHostUniqueId().toString()); - statement.setInt(2, hostStack.getCreateDuplicates()); - statement.setInt(3, hostStack.getId()); - statement.executeUpdate(); - } catch (Exception ex) { - ex.printStackTrace(); - } - }); - } - - public void deleteHost(ColdEntityStack stack) { - this.runAsync(() -> { - try (Connection connection = this.databaseConnector.getConnection()) { - String deleteHost = "DELETE FROM " + this.getTablePrefix() + "host_entities WHERE id = ?"; - try (PreparedStatement statement = connection.prepareStatement(deleteHost)) { - statement.setInt(1, stack.getId()); - statement.executeUpdate(); - } - - String deleteStackedEntities = "DELETE FROM " + this.getTablePrefix() + "stacked_entities WHERE host = ?"; - try (PreparedStatement statement = connection.prepareStatement(deleteStackedEntities)) { - statement.setInt(1, stack.getId()); - statement.executeUpdate(); - } - } catch (Exception ex) { - ex.printStackTrace(); - } - }); - } - - public void deleteStackedEntity(UUID uuid) { - if (uuid == null) - return; - - this.runAsync(() -> { - try (Connection connection = this.databaseConnector.getConnection()) { - String deleteStackedEntity = "DELETE FROM " + this.getTablePrefix() + "stacked_entities WHERE uuid = ?"; - PreparedStatement statement = connection.prepareStatement(deleteStackedEntity); - statement.setString(1, uuid.toString()); - statement.executeUpdate(); - } catch (Exception ex) { - ex.printStackTrace(); - } - }); - } - - public void deleteStackedEntities(List entities) { - this.runAsync(() -> { - try (Connection connection = this.databaseConnector.getConnection()) { - String deleteStackedEntities = "DELETE FROM " + this.getTablePrefix() + "stacked_entities WHERE uuid = ?"; - PreparedStatement statement = connection.prepareStatement(deleteStackedEntities); - for (StackedEntity entity : entities) { - if (entity == null) continue; - statement.setString(1, entity.getUniqueId().toString()); - statement.addBatch(); - } - statement.executeBatch(); - } catch (Exception ex) { - ex.printStackTrace(); - } - }); - } - public void deleteSpawner(SpawnerStack spawnerStack) { this.runAsync(() -> { try (Connection connection = this.databaseConnector.getConnection()) { @@ -272,76 +147,6 @@ public class DataManager extends DataManagerAbstract { }); } - public void getEntities(Consumer> callback) { - this.runAsync(() -> { - try (Connection connection = this.databaseConnector.getConnection()) { - - Map entities = new HashMap<>(); - - boolean mysql = Settings.MYSQL_ENABLED.getBoolean(); - int databasePurge = Settings.DATABASE_PURGE.getInt(); - String whereStatement = mysql ? "WHERE updated_at < NOW() - INTERVAL " + databasePurge + " DAY" : "WHERE updated_at <= date('now','-" + databasePurge + " day')"; - String selectOldEntities = "SELECT * FROM " + this.getTablePrefix() + "host_entities " + whereStatement; - - try (Statement statement = connection.createStatement()) { - List toDelete = new ArrayList<>(); - - ResultSet result = statement.executeQuery(selectOldEntities); - while (result.next()) { - int hostId = result.getInt("id"); - toDelete.add(String.valueOf(hostId)); - } - - if (!toDelete.isEmpty()) { - statement.execute("DELETE FROM " + this.getTablePrefix() + "host_entities " + whereStatement); - statement.execute("DELETE FROM " + this.getTablePrefix() + "stacked_entities WHERE host IN (" + String.join(", ", toDelete) + ")"); - } - } catch (Exception e) { - e.printStackTrace(); - } - - - String selectEntities = "SELECT * FROM " + this.getTablePrefix() + "host_entities"; - try (Statement statement = connection.createStatement()) { - ResultSet result = statement.executeQuery(selectEntities); - while (result.next()) { - int hostId = result.getInt("id"); - - UUID host = UUID.fromString(result.getString("uuid")); - - int createDuplicates = result.getInt("create_duplicates"); - - ColdEntityStack stack = new ColdEntityStack(host, hostId); - stack.createDuplicates(createDuplicates); - entities.put(hostId, stack); - } - } catch (Exception e) { - e.printStackTrace(); - } - - String selectStackedEntities = "SELECT * FROM " + this.getTablePrefix() + "stacked_entities"; - try (Statement statement = connection.createStatement()) { - ResultSet result = statement.executeQuery(selectStackedEntities); - while (result.next()) { - UUID uuid = UUID.fromString(result.getString("uuid")); - int hostId = result.getInt("host"); - byte[] serializedEntity = result.getBytes("serialized_entity"); - - ColdEntityStack stack = entities.get(hostId); - if (stack == null) continue; - stack.addEntityToStackSilently(new StackedEntity(uuid, serializedEntity)); - } - } catch (Exception e) { - e.printStackTrace(); - } - - this.sync(() -> callback.accept(entities)); - } catch (Exception ex) { - ex.printStackTrace(); - } - }); - } - public void getSpawners(Consumer> callback) { this.runAsync(() -> { try (Connection connection = this.databaseConnector.getConnection()){ diff --git a/src/main/java/com/songoda/ultimatestacker/database/migrations/_2_EntityStacks.java b/src/main/java/com/songoda/ultimatestacker/database/migrations/_2_EntityStacks.java index 1427518..4ab17b0 100644 --- a/src/main/java/com/songoda/ultimatestacker/database/migrations/_2_EntityStacks.java +++ b/src/main/java/com/songoda/ultimatestacker/database/migrations/_2_EntityStacks.java @@ -26,14 +26,5 @@ public class _2_EntityStacks extends DataMigration { "create_duplicates INTEGER NOT NULL DEFAULT 0" + ")"); } - - // Create stacked entities table - try (Statement statement = connection.createStatement()) { - statement.execute("CREATE TABLE IF NOT EXISTS " + tablePrefix + "stacked_entities (" + - "uuid VARCHAR(36) PRIMARY KEY NOT NULL," + - "host INTEGER NOT NULL," + - "serialized_entity VARBINARY(9999) NOT NULL" + - ")"); - } } } diff --git a/src/main/java/com/songoda/ultimatestacker/database/migrations/_5_StackedEntitiesTableUpdate.java b/src/main/java/com/songoda/ultimatestacker/database/migrations/_5_StackedEntitiesTableUpdate.java deleted file mode 100644 index a485421..0000000 --- a/src/main/java/com/songoda/ultimatestacker/database/migrations/_5_StackedEntitiesTableUpdate.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.songoda.ultimatestacker.database.migrations; - -import com.songoda.core.database.DataMigration; - -import java.sql.Connection; -import java.sql.SQLException; -import java.sql.Statement; - -public class _5_StackedEntitiesTableUpdate extends DataMigration { - - public _5_StackedEntitiesTableUpdate() { - super(5); - } - - @Override - public void migrate(Connection connection, String tablePrefix) throws SQLException { - try (Statement statement = connection.createStatement()) { - statement.execute("ALTER TABLE " + tablePrefix + "stacked_entities MODIFY serialized_entity VARBINARY(9999)"); - } catch (SQLException e) { - // Ignore - //TODO fix it for sqlite - } - } -} diff --git a/src/main/java/com/songoda/ultimatestacker/database/migrations/_4_DataPurge.java b/src/main/java/com/songoda/ultimatestacker/database/migrations/_6_RemoveStackedEntityTable.java similarity index 52% rename from src/main/java/com/songoda/ultimatestacker/database/migrations/_4_DataPurge.java rename to src/main/java/com/songoda/ultimatestacker/database/migrations/_6_RemoveStackedEntityTable.java index 68c2331..ecf5bcc 100644 --- a/src/main/java/com/songoda/ultimatestacker/database/migrations/_4_DataPurge.java +++ b/src/main/java/com/songoda/ultimatestacker/database/migrations/_6_RemoveStackedEntityTable.java @@ -1,25 +1,23 @@ package com.songoda.ultimatestacker.database.migrations; import com.songoda.core.database.DataMigration; -import com.songoda.core.database.MySQLConnector; -import com.songoda.ultimatestacker.UltimateStacker; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; -public class _4_DataPurge extends DataMigration { +public class _6_RemoveStackedEntityTable extends DataMigration { - public _4_DataPurge() { - super(4); + public _6_RemoveStackedEntityTable() { + super(6); } @Override - public void migrate(Connection connection, String tablePrefix) throws SQLException { + public void migrate(Connection connection, String tablePrefix) { try (Statement statement = connection.createStatement()) { - statement.execute("ALTER TABLE " + tablePrefix + "host_entities ADD COLUMN updated_at datetime DEFAULT NULL"); + statement.execute("DROP TABLE " + tablePrefix + "stacked_entities"); } catch (SQLException e) { - // Ignore + e.printStackTrace(); } } } diff --git a/src/main/java/com/songoda/ultimatestacker/gui/GUIConvertWhat.java b/src/main/java/com/songoda/ultimatestacker/gui/GUIConvertWhat.java index 51d62b8..bcb0269 100644 --- a/src/main/java/com/songoda/ultimatestacker/gui/GUIConvertWhat.java +++ b/src/main/java/com/songoda/ultimatestacker/gui/GUIConvertWhat.java @@ -55,7 +55,7 @@ public class GUIConvertWhat extends Gui { private void run(Player player) { if (entities) { convertFrom.convertEntities(); - UltimateStacker.getInstance().getEntityStackManager().tryAndLoadColdEntities(); + //UltimateStacker.getInstance().getEntityStackManager().tryAndLoadColdEntities(); } if (spawners) { convertFrom.convertSpawners(); diff --git a/src/main/java/com/songoda/ultimatestacker/listeners/ChunkListeners.java b/src/main/java/com/songoda/ultimatestacker/listeners/ChunkListeners.java deleted file mode 100644 index 95b58d0..0000000 --- a/src/main/java/com/songoda/ultimatestacker/listeners/ChunkListeners.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.songoda.ultimatestacker.listeners; - -import com.songoda.ultimatestacker.stackable.entity.EntityStackManager; -import org.bukkit.Chunk; -import org.bukkit.entity.Entity; -import org.bukkit.entity.LivingEntity; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.world.ChunkLoadEvent; -import org.bukkit.event.world.ChunkUnloadEvent; - -public class ChunkListeners implements Listener { - - private final EntityStackManager entityStackManager; - - public ChunkListeners(EntityStackManager entityStackManager) { - this.entityStackManager = entityStackManager; - } - - @EventHandler - public void onChunkLoad(ChunkLoadEvent event) { - Chunk chunk = event.getChunk(); - for (Entity entity : chunk.getEntities()) { - if (!(entity instanceof LivingEntity)) continue; - if (entityStackManager.isEntityInColdStorage((LivingEntity) entity)) { - entityStackManager.loadStack((LivingEntity) entity); - } - } - } - - @EventHandler - public void onChunkUnload(ChunkUnloadEvent event) { - Chunk chunk = event.getChunk(); - for (Entity entity : chunk.getEntities()) { - if (!(entity instanceof LivingEntity)) continue; - if (entityStackManager.isStackedAndLoaded((LivingEntity) entity)) { - entityStackManager.unloadStack((LivingEntity) entity); - } - } - } -} diff --git a/src/main/java/com/songoda/ultimatestacker/listeners/ClearLagListeners.java b/src/main/java/com/songoda/ultimatestacker/listeners/ClearLagListeners.java index d87a293..360c43c 100644 --- a/src/main/java/com/songoda/ultimatestacker/listeners/ClearLagListeners.java +++ b/src/main/java/com/songoda/ultimatestacker/listeners/ClearLagListeners.java @@ -18,8 +18,8 @@ public class ClearLagListeners implements Listener { @EventHandler public void onClearLaggTask(EntityRemoveEvent event) { for (Entity entity : event.getWorld().getEntities()) { - if (entity instanceof LivingEntity && plugin.getEntityStackManager().isStackedAndLoaded((LivingEntity)entity)) { - plugin.getEntityStackManager().removeStack(entity); + if (entity instanceof LivingEntity && plugin.getEntityStackManager().isStackedEntity(entity)) { + plugin.getEntityStackManager().getStack((LivingEntity) entity).destroy(); event.addEntity(entity); } } diff --git a/src/main/java/com/songoda/ultimatestacker/listeners/DeathListeners.java b/src/main/java/com/songoda/ultimatestacker/listeners/DeathListeners.java index 714184a..48744ad 100644 --- a/src/main/java/com/songoda/ultimatestacker/listeners/DeathListeners.java +++ b/src/main/java/com/songoda/ultimatestacker/listeners/DeathListeners.java @@ -82,7 +82,7 @@ public class DeathListeners implements Listener { drops.clear(); if (plugin.getCustomEntityManager().getCustomEntity(entity) == null) { - if (plugin.getEntityStackManager().isStackedAndLoaded(event.getEntity())) { + if (plugin.getEntityStackManager().isStackedEntity(event.getEntity())) { plugin.getEntityStackManager().getStack(event.getEntity()).onDeath(entity, drops, custom, event.getDroppedExp(), event); } else { DropUtils.processStackedDrop(event.getEntity(), drops, event); @@ -157,7 +157,7 @@ public class DeathListeners implements Listener { if (!(event.getEntity() instanceof LivingEntity)) return; LivingEntity entity = (LivingEntity) event.getEntity(); - if (!plugin.getEntityStackManager().isStackedAndLoaded(entity)) return; + if (!plugin.getEntityStackManager().isStackedEntity(entity)) return; EntityStack stack = plugin.getEntityStackManager().getStack(entity); Player player = (Player) event.getDamager(); diff --git a/src/main/java/com/songoda/ultimatestacker/listeners/InteractListeners.java b/src/main/java/com/songoda/ultimatestacker/listeners/InteractListeners.java index 8f44abd..0d1b2ee 100644 --- a/src/main/java/com/songoda/ultimatestacker/listeners/InteractListeners.java +++ b/src/main/java/com/songoda/ultimatestacker/listeners/InteractListeners.java @@ -38,7 +38,7 @@ public class InteractListeners implements Listener { ItemStack item = player.getInventory().getItemInHand(); - if (!plugin.getEntityStackManager().isStackedAndLoaded(entity)) return; + if (!plugin.getEntityStackManager().isStackedEntity(entity)) return; if (item.getType() != Material.NAME_TAG && !correctFood(item, entity)) return; diff --git a/src/main/java/com/songoda/ultimatestacker/listeners/ShearListeners.java b/src/main/java/com/songoda/ultimatestacker/listeners/ShearListeners.java index 6af0d97..70ee389 100644 --- a/src/main/java/com/songoda/ultimatestacker/listeners/ShearListeners.java +++ b/src/main/java/com/songoda/ultimatestacker/listeners/ShearListeners.java @@ -38,7 +38,7 @@ public class ShearListeners implements Listener { && entity.getType() != EntityType.MUSHROOM_COW && entity.getType() != EntityType.SNOWMAN) return; EntityStackManager stackManager = plugin.getEntityStackManager(); - if (!stackManager.isStackedAndLoaded(entity)) return; + if (!stackManager.isStackedEntity(entity)) return; if (event.getEntity().getType() == EntityType.SHEEP && Settings.SPLIT_CHECKS.getStringList().stream().noneMatch(line -> Split.valueOf(line) == Split.SHEEP_SHEAR) diff --git a/src/main/java/com/songoda/ultimatestacker/listeners/SheepDyeListeners.java b/src/main/java/com/songoda/ultimatestacker/listeners/SheepDyeListeners.java index 5c7b396..1fe55dd 100644 --- a/src/main/java/com/songoda/ultimatestacker/listeners/SheepDyeListeners.java +++ b/src/main/java/com/songoda/ultimatestacker/listeners/SheepDyeListeners.java @@ -24,7 +24,7 @@ public class SheepDyeListeners implements Listener { LivingEntity entity = event.getEntity(); EntityStackManager stackManager = plugin.getEntityStackManager(); - if (!stackManager.isStackedAndLoaded(entity)) return; + if (!stackManager.isStackedEntity(entity)) return; if (Settings.SPLIT_CHECKS.getStringList().stream().noneMatch(line -> Split.valueOf(line) == Split.SHEEP_DYE)) return; diff --git a/src/main/java/com/songoda/ultimatestacker/listeners/SpawnerListeners.java b/src/main/java/com/songoda/ultimatestacker/listeners/SpawnerListeners.java index 4ccb69d..c1449c8 100644 --- a/src/main/java/com/songoda/ultimatestacker/listeners/SpawnerListeners.java +++ b/src/main/java/com/songoda/ultimatestacker/listeners/SpawnerListeners.java @@ -67,12 +67,16 @@ public class SpawnerListeners implements Listener { SpawnerStack spawnerStack = spawnerStackManager.getSpawner(location); - spawnerStack.spawn(spawnerStack.calculateSpawnCount(), "EXPLOSION_NORMAL", null, (e) -> { + int amountToSpawn = spawnerStack.calculateSpawnCount(); + + spawnerStack.spawn(amountToSpawn, "EXPLOSION_NORMAL", null, (e) -> { if (Settings.NO_AI.getBoolean()) EntityUtils.setUnaware(e); if (mcmmo) entity.setMetadata("mcMMO: Spawned Entity", new FixedMetadataValue(plugin, true)); + + UltimateStacker.getInstance().getEntityStackManager().setStack(e, amountToSpawn); return true; }, event.getEntityType()); } diff --git a/src/main/java/com/songoda/ultimatestacker/listeners/TameListeners.java b/src/main/java/com/songoda/ultimatestacker/listeners/TameListeners.java index d993b4e..ec93ed4 100644 --- a/src/main/java/com/songoda/ultimatestacker/listeners/TameListeners.java +++ b/src/main/java/com/songoda/ultimatestacker/listeners/TameListeners.java @@ -22,7 +22,7 @@ public class TameListeners implements Listener { LivingEntity entity = event.getEntity(); EntityStackManager stackManager = plugin.getEntityStackManager(); - if (!stackManager.isStackedAndLoaded(entity)) return; + if (!stackManager.isStackedEntity(entity)) return; EntityStack stack = plugin.getEntityStackManager().getStack(entity); diff --git a/src/main/java/com/songoda/ultimatestacker/listeners/entity/EntityCurrentListener.java b/src/main/java/com/songoda/ultimatestacker/listeners/entity/EntityCurrentListener.java index 66b61f2..05a1f04 100644 --- a/src/main/java/com/songoda/ultimatestacker/listeners/entity/EntityCurrentListener.java +++ b/src/main/java/com/songoda/ultimatestacker/listeners/entity/EntityCurrentListener.java @@ -20,13 +20,11 @@ public class EntityCurrentListener implements Listener { @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onSpawn(EntityTransformEvent event) { EntityStackManager stackManager = plugin.getEntityStackManager(); - if (stackManager.isStackedAndLoaded(event.getEntity().getUniqueId()) + if (stackManager.isStackedEntity(event.getEntity()) && event.getEntity() instanceof LivingEntity && event.getTransformedEntity() instanceof LivingEntity) { - EntityStack stack = stackManager.updateStack((LivingEntity) event.getEntity(), - (LivingEntity) event.getTransformedEntity()); + EntityStack stack = stackManager.updateStack((LivingEntity) event.getEntity(), (LivingEntity) event.getTransformedEntity()); stack.releaseHost(); - stack.updateStack(); } } } 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 bfc0f1f..fe3b937 100644 --- a/src/main/java/com/songoda/ultimatestacker/listeners/entity/EntityListeners.java +++ b/src/main/java/com/songoda/ultimatestacker/listeners/entity/EntityListeners.java @@ -59,7 +59,7 @@ public class EntityListeners implements Listener { EntityStackManager stackManager = plugin.getEntityStackManager(); - if (!stackManager.isStackedAndLoaded(entity)) return; + if (!stackManager.isStackedEntity(entity)) return; EntityStack stack = stackManager.getStack(entity); @@ -78,7 +78,7 @@ public class EntityListeners implements Listener { Entity entity = event.getEntity(); - if (entity instanceof LivingEntity && plugin.getEntityStackManager().isStackedAndLoaded((LivingEntity) entity) + if (entity instanceof LivingEntity && 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/ColdEntityStack.java b/src/main/java/com/songoda/ultimatestacker/stackable/entity/ColdEntityStack.java deleted file mode 100644 index 62d3dd4..0000000 --- a/src/main/java/com/songoda/ultimatestacker/stackable/entity/ColdEntityStack.java +++ /dev/null @@ -1,169 +0,0 @@ -package com.songoda.ultimatestacker.stackable.entity; - -import com.songoda.core.nms.NmsManager; -import com.songoda.core.nms.nbt.NBTEntity; -import com.songoda.ultimatestacker.UltimateStacker; -import com.songoda.ultimatestacker.stackable.entity.custom.CustomEntity; -import com.songoda.ultimatestacker.utils.Stackable; -import org.bukkit.Location; -import org.bukkit.entity.Entity; -import org.bukkit.entity.LivingEntity; - -import java.util.Deque; -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.ConcurrentLinkedDeque; -import java.util.stream.Collectors; - -public class ColdEntityStack implements Stackable { - - protected static final UltimateStacker plugin = UltimateStacker.getInstance(); - - // The id to identify this stack in the database. - private int id; - - // The unique id of the stacks host. - protected UUID hostUniqueId; - - // These are the entities below the host. - protected final Deque stackedEntities = new ConcurrentLinkedDeque<>(); - - // The amount of duplicates of the host to create when loaded. - protected int createDuplicates = 0; - - public ColdEntityStack(UUID hostUniqueId, int id) { - this.hostUniqueId = hostUniqueId; - this.id = id; - } - - public ColdEntityStack(UUID hostUniqueId) { - this.hostUniqueId = hostUniqueId; - } - - public StackedEntity addEntityToStackSilently(Entity entity) { - return addEntityToStackSilently(getStackedEntity(entity)); - } - - public List addRawEntitiesToStackSilently(List unstackedEntities) { - List stackedEntities = unstackedEntities.stream() - .map(this::getStackedEntity).collect(Collectors.toList()); - addEntitiesToStackSilently(stackedEntities); - return stackedEntities; - } - - public void addEntitiesToStackSilently(List stackedEntities) { - stackedEntities.removeIf(Objects::isNull); - this.stackedEntities.addAll(stackedEntities); - } - - public synchronized StackedEntity addEntityToStackSilently(StackedEntity stackedEntity) { - if (stackedEntity == null) return null; - stackedEntities.push(stackedEntity); - return stackedEntity; - } - - public void moveEntitiesFromStack(EntityStack stack, int amount) { - List stackedEntities = stack.takeEntities(amount); - this.stackedEntities.addAll(stackedEntities); - plugin.getDataManager().createStackedEntities(this, stackedEntities); - } - - public List takeEntities(int amount) { - List entities = new LinkedList<>(); - for (int i = 0; i < amount; i++) { - StackedEntity entity = stackedEntities.pollFirst(); - if (entity != null) - entities.add(entity); - } - plugin.getDataManager().deleteStackedEntities(entities); - return entities; - } - - public List takeAllEntities() { - List entities = new LinkedList<>(stackedEntities); - stackedEntities.clear(); - return entities; - } - - public LivingEntity takeOneAndSpawnEntity(Location location) { - if (stackedEntities.isEmpty()) return null; - NBTEntity nbtEntity = NmsManager.getNbt().newEntity(); - nbtEntity.deSerialize(stackedEntities.getFirst().getSerializedEntity()); - - for (CustomEntity customEntity : plugin.getCustomEntityManager().getRegisteredCustomEntities()) { - String identifier = customEntity.getPluginName() + "_UltimateStacker"; - if (!nbtEntity.has(identifier)) continue; - LivingEntity entity = customEntity.spawnFromIdentifier(nbtEntity.getString(identifier), location); - if (entity == null) continue; - stackedEntities.removeFirst(); - plugin.getDataManager().deleteStackedEntity(entity.getUniqueId()); - return entity; - } - - LivingEntity newEntity = null; - for (int i = 0; i < 5; i++) { - newEntity = (LivingEntity) nbtEntity.spawn(location); - - if (newEntity != null) { - stackedEntities.removeFirst(); - plugin.getDataManager().deleteStackedEntity(newEntity.getUniqueId()); - break; - } - } - if (newEntity == null) - plugin.getDataManager().deleteStackedEntity(hostUniqueId); - - return newEntity; - } - - @Override - public int getAmount() { - return stackedEntities.size() + 1; - } - - @Override - public boolean isValid() { - return true; - } - - public void createDuplicates(int duplicates) { - this.createDuplicates = duplicates; - } - - public int getCreateDuplicates() { - return createDuplicates; - } - - protected StackedEntity getStackedEntity(Entity entity) { - return getStackedEntity(entity, false); - } - - protected synchronized StackedEntity getStackedEntity(Entity entity, boolean newUUID) { - if (entity == null) return null; - UUID uuid = entity.getUniqueId(); - NBTEntity nbtEntity = NmsManager.getNbt().of(entity); - - CustomEntity customEntity = plugin.getCustomEntityManager().getCustomEntity(entity); - if (customEntity != null) - nbtEntity.set(customEntity.getPluginName() + "_UltimateStacker", customEntity.getNBTIdentifier(entity)); - return new StackedEntity(uuid, nbtEntity.serialize()); - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public UUID getHostUniqueId() { - return hostUniqueId; - } - - public void setHostUniqueId(UUID hostUniqueId) { - this.hostUniqueId = hostUniqueId; - } -} 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 e2de50a..d1f2ffd 100644 --- a/src/main/java/com/songoda/ultimatestacker/stackable/entity/EntityStack.java +++ b/src/main/java/com/songoda/ultimatestacker/stackable/entity/EntityStack.java @@ -3,9 +3,12 @@ package com.songoda.ultimatestacker.stackable.entity; import com.songoda.core.compatibility.ServerVersion; import com.songoda.core.lootables.loot.Drop; import com.songoda.core.lootables.loot.DropUtils; +import com.songoda.core.nms.NmsManager; +import com.songoda.core.nms.nbt.NBTEntity; import com.songoda.ultimatestacker.UltimateStacker; import com.songoda.ultimatestacker.events.EntityStackKillEvent; import com.songoda.ultimatestacker.settings.Settings; +import com.songoda.ultimatestacker.stackable.entity.custom.CustomEntity; import com.songoda.ultimatestacker.utils.Async; import com.songoda.ultimatestacker.utils.Methods; import org.bukkit.Bukkit; @@ -21,96 +24,42 @@ import org.bukkit.util.Vector; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.concurrent.ThreadLocalRandom; -public class EntityStack extends ColdEntityStack { +public class EntityStack extends StackedEntity { - // This is the host entity which is not stored in serialized nbt. - private LivingEntity hostEntity; + private static final UltimateStacker plugin = UltimateStacker.getInstance(); public EntityStack(LivingEntity hostEntity) { - super(hostEntity.getUniqueId()); - this.hostEntity = hostEntity; + super(hostEntity); } - public EntityStack(LivingEntity hostEntity, ColdEntityStack coldEntityStack) { - this(hostEntity); - this.setId(coldEntityStack.getId()); - this.stackedEntities.addAll(coldEntityStack.stackedEntities); + public EntityStack(LivingEntity hostEntity, int amount) { + super(hostEntity, amount); } - public StackedEntity addEntityToStack(Entity entity) { - StackedEntity stackedEntity = addEntityToStackSilently(entity); - updateStack(); - return stackedEntity; + public synchronized EntityStack addEntityToStack(int amount) { + setAmount(getAmount() + amount); + return this; } - @Override - public List takeEntities(int amount) { - List entities = super.takeEntities(amount); - if (this.stackedEntities.isEmpty()) - destroy(true); - return entities; - } - - @Override - public List takeAllEntities() { - destroy(true); - return super.takeAllEntities(); - } - - public void updateStack() { - Async.run(() -> { - if (createDuplicates != 0) { - List stackedEntities = new ArrayList<>(); - try { - for (int i = 0; i < createDuplicates; i++) { - StackedEntity entity = addEntityToStackSilently(getStackedEntity(hostEntity, true)); - if (entity != null) - stackedEntities.add(entity); - } - plugin.getDataManager().createStackedEntities(this, stackedEntities); - - createDuplicates = 0; - updateNametag(); - } catch (Exception ignored) { - //Ignored for now - } - } - }); - updateNametag(); - } - - public void updateNametag() { - if (hostEntity == null) { - //Delay with 1 tick to make sure the entity is loaded. - Bukkit.getScheduler().scheduleSyncDelayedTask(UltimateStacker.getInstance(), this::updateNametag, 1L); - return; - } - hostEntity.setCustomNameVisible(!Settings.HOLOGRAMS_ON_LOOK_ENTITY.getBoolean()); - hostEntity.setCustomName(Methods.compileEntityName(hostEntity, getAmount())); + public synchronized EntityStack removeEntityFromStack(int amount) { + setAmount(getAmount() - amount); + return this; } public LivingEntity getHostEntity() { - if (hostEntity == null) { - plugin.getEntityStackManager().removeStack(this.hostUniqueId); - return null; - } return hostEntity; } - public StackedEntity getHostAsStackedEntity() { - return getStackedEntity(hostEntity); - } - protected void setHostEntity(LivingEntity hostEntity) { this.hostEntity = hostEntity; - this.hostUniqueId = hostEntity.getUniqueId(); } private void handleWholeStackDeath(LivingEntity killed, List drops, boolean custom, int droppedExp, EntityDeathEvent event) { - plugin.getDataManager().deleteHost(this); + EntityStack stack = plugin.getEntityStackManager().getStack(killed); // In versions 1.14 and below experience is not dropping. Because of this we are doing this ourselves. if (ServerVersion.isServerVersionAtOrBelow(ServerVersion.V1_14)) { Location killedLocation = killed.getLocation(); @@ -133,7 +82,7 @@ public class EntityStack extends ColdEntityStack { } event.getDrops().clear(); - plugin.getEntityStackManager().removeStack(event.getEntity()); + stack.destroy(); if (killed.getKiller() == null) return; plugin.addExp(killed.getKiller(), this); } @@ -162,12 +111,6 @@ public class EntityStack extends ColdEntityStack { newEntity.setVelocity(velocity); stackManager.updateStack(killed, newEntity); - - updateStack(); - - if (stackedEntities.isEmpty()) { - destroy(); - } } public void onDeath(LivingEntity killed, List drops, boolean custom, int droppedExp, EntityDeathEvent event) { @@ -195,42 +138,37 @@ public class EntityStack extends ColdEntityStack { } } + public LivingEntity takeOneAndSpawnEntity(Location location) { + if (amount <= 0) return null; + amount--; + LivingEntity entity = Objects.requireNonNull(location.getWorld()).spawn(location, hostEntity.getClass()); + this.hostEntity = entity; + updateNameTag(); + return entity; + } + public void releaseHost() { LivingEntity oldHost = hostEntity; LivingEntity entity = takeOneAndSpawnEntity(hostEntity.getLocation()); - if (!stackedEntities.isEmpty()) { - destroy(false); + if (getAmount() >= 0) { plugin.getEntityStackManager().updateStack(oldHost, entity); - entity.setVelocity(new Vector(ThreadLocalRandom.current().nextDouble(-1, 1.01), - 0, ThreadLocalRandom.current().nextDouble(-1, 1.01)).normalize().multiply(0.5)); + updateNameTag(); } else { destroy(); } - updateStack(); - } public void destroy() { - destroy(true); - } - - public void destroy(boolean full) { - if (full) - plugin.getEntityStackManager().removeStack(this.hostUniqueId); - if (hostEntity != null) { - try { - hostEntity.setCustomNameVisible(false); - hostEntity.setCustomName(null); - } catch (NullPointerException ignored) {} - } + if (hostEntity == null) return; + Bukkit.getScheduler().runTask(plugin, hostEntity::remove); hostEntity = null; - hostUniqueId = null; } @Override public String toString() { return "EntityStack{" + "hostEntity=" + hostEntity + + ", amount=" + amount + '}'; } } 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 29d8a77..8c32af6 100644 --- a/src/main/java/com/songoda/ultimatestacker/stackable/entity/EntityStackManager.java +++ b/src/main/java/com/songoda/ultimatestacker/stackable/entity/EntityStackManager.java @@ -7,6 +7,7 @@ import org.bukkit.ChatColor; import org.bukkit.Chunk; import org.bukkit.World; import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; import org.bukkit.entity.LivingEntity; import java.util.Collection; @@ -17,157 +18,92 @@ import java.util.UUID; public class EntityStackManager { - // These are all stacked mobs loaded into memory. - private static final Map stacks = new HashMap<>(); - - // This will only be used for stacks that have not yet been loaded into the game. - private static final Map coldStacks = new HashMap<>(); - private final UltimateStacker plugin; public EntityStackManager(UltimateStacker plugin) { this.plugin = plugin; } - public EntityStack addStack(EntityStack stack) { - stacks.put(stack.getHostEntity().getUniqueId(), stack); - return stack; + public EntityStack createStack(LivingEntity entity, int amount) { + return new EntityStack(entity, amount); + } + + public boolean isStackedEntity(Entity entity) { + return entity.hasMetadata("US_AMOUNT"); + } + + public int getAmount(Entity entity) { + if (!isStackedEntity(entity)) return 1; + if (entity.getMetadata("US_AMOUNT").isEmpty()) return 1; + return entity.getMetadata("US_AMOUNT").get(0).asInt(); } public EntityStack addStack(LivingEntity entity) { - if (entity == null) return null; - EntityStack stack = new EntityStack(entity); - plugin.getDataManager().createHostEntity(stack); - stacks.put(entity.getUniqueId(), stack); - return stack; + return addStack(entity, getAmount(entity) == 1 ? 1 : getAmount(entity)); } public EntityStack addStack(LivingEntity entity, int amount) { if (entity == null) return null; - EntityStack stack = new EntityStack(entity); - plugin.getDataManager().createHostEntity(stack); - stacks.put(entity.getUniqueId(), stack); - stack.createDuplicates(amount - 1); - plugin.getDataManager().updateHost(stack); - stack.updateStack(); - return stack; - } - - @Deprecated - public EntityStack addSerializedStack(LivingEntity entity, String customName) { - if (customName != null && customName.contains(String.valueOf(ChatColor.COLOR_CHAR))) { - String name = customName.replace(String.valueOf(ChatColor.COLOR_CHAR), "") - .replace(";", ""); - if (!name.contains(":")) return null; - String split = name.split(":")[0]; - int amount = Methods.isInt(split) ? Integer.parseInt(split) : 0; - return addStack(entity, amount); + if (isStackedEntity(entity)) { + EntityStack stack = getStack(entity); + stack.addEntityToStack(amount); + return stack; } return null; } - @Deprecated - public EntityStack addSerializedStack(LivingEntity entity) { - return addSerializedStack(entity, entity.getCustomName()); + public EntityStack getStack(UUID uuid) { + Entity entity = Bukkit.getEntity(uuid); + if (entity == null) return null; + if (isStackedEntity(entity)) { + if (entity instanceof LivingEntity) { + return new EntityStack((LivingEntity) entity); + } + } + return null; } public EntityStack getStack(LivingEntity entity) { - EntityStack stack = getStack(entity.getUniqueId()); - if (stack == null) stack = addSerializedStack(entity); + if (!isStackedEntity(entity)) return null; + return new EntityStack(entity); + } + + public EntityStack decreaseStack(Entity entity) { + EntityStack stack = getStack((LivingEntity) entity); + if (stack == null) return null; + stack.removeEntityFromStack(1); return stack; } - public EntityStack getStack(UUID uuid) { - return stacks.get(uuid); - } - - public EntityStack removeStack(Entity entity) { - return removeStack(entity.getUniqueId()); - } - - public EntityStack removeStack(UUID uuid) { - EntityStack stack = stacks.remove(uuid); - if (stack != null) { - plugin.getDataManager().deleteHost(stack); - stack.destroy(); - } - + public EntityStack decreaseStack(Entity entity, int amount) { + EntityStack stack = getStack((LivingEntity) entity); + if (stack == null) return null; + stack.removeEntityFromStack(amount); return stack; } - public Map getStacks() { - return Collections.unmodifiableMap(stacks); + public EntityStack updateStack(LivingEntity entity) { + EntityStack stack = getStack(entity); + if (stack == null) return null; + stack.updateNameTag(); + return stack; } public EntityStack updateStack(LivingEntity oldEntity, LivingEntity newEntity) { - EntityStack stack = stacks.remove(oldEntity.getUniqueId()); + EntityStack stack = getStack(oldEntity); if (stack == null) return null; - stack.setHostEntity(newEntity); - stacks.put(newEntity.getUniqueId(), stack); - plugin.getDataManager().updateHost(stack); - return stack; - } - - @Deprecated - public boolean isStacked(UUID entity) { - return isStackedAndLoaded(entity); - } - - public boolean isStackedAndLoaded(LivingEntity entity) { - return stacks.containsKey(entity.getUniqueId()); - } - - public boolean isStackedAndLoaded(UUID entity) { - return stacks.containsKey(entity); - } - - public boolean isEntityInColdStorage(UUID entity) { - return coldStacks.containsKey(entity); - } - - public boolean isEntityInColdStorage(LivingEntity entity) { - return isEntityInColdStorage(entity.getUniqueId()); - } - - public void loadStack(LivingEntity entity) { - ColdEntityStack coldStack = coldStacks.get(entity.getUniqueId()); - if (coldStack == null) return; - EntityStack stack = new EntityStack(entity, coldStack); - stack.updateStack(); - stacks.put(entity.getUniqueId(), stack); - plugin.getDataManager().updateHost(coldStack); - } - - public void unloadStack(LivingEntity entity) { - EntityStack stack = stacks.get(entity.getUniqueId()); - if (stack == null) return; - ColdEntityStack coldStack = new EntityStack(entity, stack); + int amount = stack.getAmount(); stack.destroy(); - coldStacks.put(entity.getUniqueId(), coldStack); + return createStack(newEntity, amount); } - public void addStacks(Collection entities) { - for (ColdEntityStack stack : entities) - coldStacks.put(stack.hostUniqueId, stack); - } - - public ColdEntityStack addLegacyColdStack(UUID entity, int amount) { - ColdEntityStack stack = new ColdEntityStack(entity); - plugin.getDataManager().createHostEntity(stack); - stack.createDuplicates(amount - 1); - plugin.getDataManager().updateHost(stack); - coldStacks.put(entity, stack); - return stack; - } - - public void tryAndLoadColdEntities() { - for (World world : Bukkit.getWorlds()) { - for (Chunk chunk : world.getLoadedChunks()) { - for (Entity entity : chunk.getEntities()) { - if (entity instanceof LivingEntity) - loadStack((LivingEntity)entity); - } - } + public void setStack(LivingEntity newEntity, int amount) { + if (isStackedEntity(newEntity)) { + EntityStack stack = getStack(newEntity); + stack.setAmount(amount); + System.err.println("Stacked entity already exists, updating stack amount to " + amount); + } else { + createStack(newEntity, amount); } } } diff --git a/src/main/java/com/songoda/ultimatestacker/stackable/entity/StackedEntity.java b/src/main/java/com/songoda/ultimatestacker/stackable/entity/StackedEntity.java index 189ee53..0558b97 100644 --- a/src/main/java/com/songoda/ultimatestacker/stackable/entity/StackedEntity.java +++ b/src/main/java/com/songoda/ultimatestacker/stackable/entity/StackedEntity.java @@ -1,22 +1,78 @@ package com.songoda.ultimatestacker.stackable.entity; +import com.songoda.ultimatestacker.UltimateStacker; +import com.songoda.ultimatestacker.settings.Settings; +import com.songoda.ultimatestacker.utils.Methods; +import org.bukkit.Bukkit; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.metadata.FixedMetadataValue; + import java.util.UUID; public class StackedEntity { - private final UUID uniqueId; - private final byte[] serializedEntity; + protected int amount; + protected LivingEntity hostEntity; - public StackedEntity(UUID uniqueId, byte[] serializedEntity) { - this.uniqueId = uniqueId; - this.serializedEntity = serializedEntity; + /** + * Gets an existing stack from an entity or creates a new one if it doesn't exist. + * @param entity The entity to get the stack from. + */ + public StackedEntity(LivingEntity entity) { + if (entity == null) return; + if (!UltimateStacker.getInstance().getEntityStackManager().isStackedEntity(entity)) { + entity.setMetadata("US_AMOUNT", new FixedMetadataValue(UltimateStacker.getInstance(), 1)); + this.amount = 1; + updateNameTag(); + } else { + //get the amount from the entity + this.amount = UltimateStacker.getInstance().getEntityStackManager().getAmount(entity); + } + this.hostEntity = entity; } - public UUID getUniqueId() { - return uniqueId; + /** + * Creates a new stack or overrides an existing stack. + * @param entity The entity to create the stack for. + * @param amount The amount of entities in the stack. + */ + public StackedEntity(LivingEntity entity, int amount) { + if (entity == null) return; + this.hostEntity = entity; + this.amount = amount; + entity.setMetadata("US_AMOUNT", new FixedMetadataValue(UltimateStacker.getInstance(), amount)); + updateNameTag(); } - public byte[] getSerializedEntity() { - return serializedEntity; + + public EntityType getType() { + return hostEntity.getType(); + } + + public int getAmount() { + return amount; + } + + public void setAmount(int amount) { + this.amount = amount; + this.hostEntity.setMetadata("US_AMOUNT", new FixedMetadataValue(UltimateStacker.getInstance(), amount)); + updateNameTag(); + } + + public UUID getUuid() { + return hostEntity.getUniqueId(); + } + + public LivingEntity getHostEntity() { + return hostEntity; + } + + protected void updateNameTag() { + if (hostEntity == null) { + return; + } + hostEntity.setCustomNameVisible(!Settings.HOLOGRAMS_ON_LOOK_ENTITY.getBoolean()); + hostEntity.setCustomName(Methods.compileEntityName(hostEntity, getAmount())); } } diff --git a/src/main/java/com/songoda/ultimatestacker/tasks/StackingTask.java b/src/main/java/com/songoda/ultimatestacker/tasks/StackingTask.java index 6bbca63..c409712 100644 --- a/src/main/java/com/songoda/ultimatestacker/tasks/StackingTask.java +++ b/src/main/java/com/songoda/ultimatestacker/tasks/StackingTask.java @@ -58,11 +58,14 @@ import java.util.Set; 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; -public class StackingTask extends BukkitRunnable { +public class StackingTask implements Runnable { private final UltimateStacker plugin; + private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(4); private final EntityStackManager stackManager; @@ -96,7 +99,12 @@ public class StackingTask extends BukkitRunnable { loadedWorlds.add(new SWorld(world)); // 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); + } + + public void stop() { + executorService.shutdown(); } @Override @@ -197,32 +205,46 @@ public class StackingTask extends BukkitRunnable { } - private void processEntity(LivingEntity livingEntity, SWorld sWorld, Location location) { + private void processEntity(LivingEntity baseEntity, SWorld sWorld, Location location) { + + // Check our WorldGuard flag. + Boolean flag = WorldGuardHook.isEnabled() ? WorldGuardHook.getBooleanFlag(baseEntity.getLocation(), "mob-stacking") : null; + if (flag != null && !flag) { + return; + } + // Get the stack from the entity. It should be noted that this value will // be null if our entity is not a stack. - EntityStack stack = plugin.getEntityStackManager().getStack(livingEntity); - - // Is this entity stacked? - boolean isStack = stack != null; - - // The amount that is stackable. - int amountToStack = isStack ? stack.getAmount() : 1; - - // 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, a custom entity or disabled then skip it. - if (!isStack && (livingEntity.getCustomName() != null - && plugin.getCustomEntityManager().getCustomEntity(livingEntity) == null) - || !configurationSection.getBoolean("Mobs." + livingEntity.getType().name() + ".Enabled")) - return; + EntityStack baseStack = plugin.getEntityStackManager().getStack(baseEntity); // Get the maximum stack size for this entity. - int maxEntityStackSize = getEntityStackSize(livingEntity); + int maxEntityStackSize = getEntityStackSize(baseEntity); + + // Is this entity stacked? + boolean isStack = baseStack != null; + + if (isStack && baseStack.getAmount() == maxEntityStackSize) { + // If the stack is already at the max size then we can skip it. + return; + } + + // The amount that is stackable. + int amountToStack = isStack ? baseStack.getAmount() : 1; + + // Attempt to split our stack. If the split is successful then skip this entity. + if (isStack && attemptSplit(baseStack, baseEntity)) return; + + // 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")) + return; + + // Get similar entities around our entity and make sure those entities are both compatible and stackable. List stackableFriends = new LinkedList<>(); - for (LivingEntity entity : getSimilarEntitiesAroundEntity(livingEntity, sWorld, location)) { + for (LivingEntity entity : getSimilarEntitiesAroundEntity(baseEntity, sWorld, location)) { // Check to see if entity is not stackable. if (!isEntityStackable(entity)) continue; @@ -231,156 +253,56 @@ public class StackingTask extends BukkitRunnable { } // 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; - - // Check our WorldGuard flag. - Boolean flag = WorldGuardHook.isEnabled() ? WorldGuardHook.getBooleanFlag(livingEntity.getLocation(), "mob-stacking") : null; - if (flag != null && !flag) - continue; + for (LivingEntity friendlyEntity : stackableFriends) { + // Make sure the friendlyEntity has not already been processed. + if (this.processed.contains(friendlyEntity.getUniqueId())) continue; // Get this entities friendStack. - EntityStack friendStack = stackManager.getStack(entity); + EntityStack friendStack = stackManager.getStack(friendlyEntity); - // Check to see if this entity is stacked and friendStack plus + if (friendStack == null) continue; + + // Check to see if this friendlyEntity 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) { + // for this friendlyEntity. - // If we are a stack lets merge our stack with the just found friend stack. - if (isStack) { - // Get the host entity. - StackedEntity host = stack.getHostAsStackedEntity(); - // Get all the stacked entities in our stack and add them to a list. - List entities = stack.takeAllEntities(); - // Add the host to this list. - entities.add(host); - // Add the collected entities to the new stack. - friendStack.addEntitiesToStackSilently(entities); - // Update friend stack to display changes. - friendStack.updateStack(); - // Push changes to the database. - plugin.getDataManager().createStackedEntities(friendStack, entities); - } else { - // If we are not stacked add ourselves to the found friendStack. - plugin.getDataManager().createStackedEntity(friendStack, friendStack.addEntityToStack(livingEntity)); + boolean overstack = (friendStack.getAmount() + 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(); } - - // Drop lead if applicable then remove our entity and mark it as processed. - if (livingEntity.isLeashed()) - Bukkit.getScheduler().runTask(plugin, () -> livingEntity.getWorld() - .dropItemNaturally(livingEntity.getLocation(), CompatibleMaterial.LEAD.getItem())); - Bukkit.getScheduler().runTask(plugin, livingEntity::remove); - processed.add(livingEntity.getUniqueId()); - - return; - } else if (friendStack == null - && isStack - && (stack.getAmount() + 1) <= maxEntityStackSize - && canFly(entity) - && Settings.ONLY_STACK_FLYING_DOWN.getBoolean() - && location.getY() > entity.getLocation().getY()) { - - // 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)); - - // Remove our entity and mark it as processed. - Bukkit.getScheduler().runTask(plugin, livingEntity::remove); - processed.add(livingEntity.getUniqueId()); + processed.add(baseEntity.getUniqueId()); return; } } - - // If our entity is stacked then skip this entity. - if (isStack) return; - - // Check our WorldGuard flag. - Boolean flag = WorldGuardHook.isEnabled() ? WorldGuardHook.getBooleanFlag(livingEntity.getLocation(), "mob-stacking") : null; - if (flag != null && !flag) - return; - - // Remove all stacked entities from our stackable friends. - stackableFriends.removeIf(stackManager::isStackedAndLoaded); - - // If the stack cap is met then delete this entity. - if (maxPerTypeStacksPerChunk != -1 - && (getSimilarStacksInChunk(sWorld, livingEntity) + 1) > maxPerTypeStacksPerChunk) { - Bukkit.getScheduler().runTask(plugin, livingEntity::remove); - this.processed.add(livingEntity.getUniqueId()); - return; - } - - // 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; - - // If a stack was never found create a new one. - EntityStack newStack = stackManager.addStack(livingEntity); - - List livingEntities = new LinkedList<>(); - - // Loop through the unstacked and unprocessed stackable friends while not creating - // a stack larger than the maximum. - stackableFriends.stream().filter(entity -> !stackManager.isStackedAndLoaded(entity) - && !this.processed.contains(entity.getUniqueId())).limit(maxEntityStackSize).forEach(entity -> { - - // Make sure we're not naming some poor kids pet. - if (entity.getCustomName() != null - && plugin.getCustomEntityManager().getCustomEntity(entity) == null) { - processed.add(livingEntity.getUniqueId()); - newStack.destroy(); - return; - } - - // Drop lead if applicable then remove our entity and mark it as processed. - if (entity.isLeashed()) { - Bukkit.getScheduler().runTask(plugin, () -> entity.getWorld().dropItemNaturally(entity.getLocation(), CompatibleMaterial.LEAD.getItem())); - } - livingEntities.add(entity); - Bukkit.getScheduler().runTask(plugin, entity::remove); - processed.add(entity.getUniqueId()); - - }); - - // Add our new approved entities to the new stack and commit them to the database. - plugin.getDataManager().createStackedEntities(newStack, - newStack.addRawEntitiesToStackSilently(livingEntities)); - - // Update our stack. - newStack.updateStack(); } - public boolean attemptSplit(EntityStack stack, LivingEntity livingEntity) { - int stackSize = stack.getAmount(); + public boolean attemptSplit(EntityStack baseStack, LivingEntity livingEntity) { + int stackSize = baseStack.getAmount(); int maxEntityStackAmount = getEntityStackSize(livingEntity); if (stackSize <= maxEntityStackAmount) return false; - // Destroy the stack. - stack.destroy(); + baseStack.setAmount(maxEntityStackAmount); Bukkit.getScheduler().runTask(plugin, () -> { - for (int i = stackSize; i > 0; i -= maxEntityStackAmount) { - LivingEntity entity = stack.takeOneAndSpawnEntity(livingEntity.getLocation()); - if (entity == null) continue; - EntityStack newStack = plugin.getEntityStackManager().addStack(entity); - newStack.moveEntitiesFromStack(stack, Math.min(i, maxEntityStackAmount) - 1); - newStack.updateStack(); - } + int finalStackSize = stackSize - maxEntityStackAmount; + do { + // Create a new stack, summon entity and add to stack. + LivingEntity newEntity = (LivingEntity) livingEntity.getWorld().spawnEntity(livingEntity.getLocation(), livingEntity.getType()); + int toAdd = Math.min(finalStackSize, maxEntityStackAmount); + EntityStack newStack = stackManager.createStack(newEntity, toAdd); + processed.add(newEntity.getUniqueId()); + finalStackSize -= maxEntityStackAmount; + } while (finalStackSize >= 0); }); // Remove our entity and mark it as processed. - Bukkit.getScheduler().runTask(plugin, livingEntity::remove); processed.add(livingEntity.getUniqueId()); return true; } @@ -443,7 +365,7 @@ public class StackingTask extends BukkitRunnable { 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().isStackedAndLoaded(e)) + if (entity.getType() == e.getType() && plugin.getEntityStackManager().isStackedEntity(e)) count++; } return count;