Merge branch 'development'

This commit is contained in:
ceze88 2023-01-25 17:26:32 +01:00
commit 005d30ac53
18 changed files with 353 additions and 81 deletions

View File

@ -20,7 +20,7 @@
* [Suggestions](#suggestions) * [Suggestions](#suggestions)
## Introduction ## Introduction
Ultimatestacker is a simple yet powerful stacking plugin for Minecraft that allows you to stack mobs, items, spawners and blocks on your server all in a single. This allows for the reduction of lag from large amounts of mobs walking around and in mob grinders, items floating on the ground, large aounts of placed spawners in grinders, and also allows you to remove randomly placed block towers in your builds and turn them in to a single block. The plugin stores all the mobs, items, spawners and blocks, and displays a single entity along with a hologram above the entity that allows you to see how many entities are in that particular stack. This functionality is sure to keep your players coming back because of the high entity caps with reduced lag. Ultimatestacker is a simple yet powerful stacking plugin for Minecraft that allows you to stack mobs, items, spawners and blocks on your server all in a single. This allows for the reduction of lag from large amounts of mobs walking around and in mob grinders, items floating on the ground, large amounts of placed spawners in grinders, and also allows you to remove randomly placed block towers in your builds and turn them in to a single block. The plugin stores all the mobs, items, spawners and blocks, and displays a single entity along with a hologram above the entity that allows you to see how many entities are in that particular stack. This functionality is sure to keep your players coming back because of the high entity caps with reduced lag.
## Marketplace ## Marketplace
You can visit [our marketplace](https://songoda.com/marketplace/product/ultimatestacker-the-simple-to-use-stacker.16) to download ultimatestacker as well as take a look at many other fantastic plugins which are sure to catch your eye. You can visit [our marketplace](https://songoda.com/marketplace/product/ultimatestacker-the-simple-to-use-stacker.16) to download ultimatestacker as well as take a look at many other fantastic plugins which are sure to catch your eye.

22
pom.xml
View File

@ -2,7 +2,7 @@
<groupId>com.songoda</groupId> <groupId>com.songoda</groupId>
<artifactId>UltimateStacker</artifactId> <artifactId>UltimateStacker</artifactId>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<version>2.3.2</version> <version>2.3.3</version>
<build> <build>
<defaultGoal>clean install</defaultGoal> <defaultGoal>clean install</defaultGoal>
<finalName>UltimateStacker-${project.version}</finalName> <finalName>UltimateStacker-${project.version}</finalName>
@ -99,6 +99,16 @@
<id>CodeMC</id> <id>CodeMC</id>
<url>https://repo.codemc.org/repository/maven-public/</url> <url>https://repo.codemc.org/repository/maven-public/</url>
</repository> </repository>
<repository>
<id>bg-repo</id>
<url>https://repo.bg-software.com/repository/api/</url>
</repository>
<repository>
<id>lumine-repo</id>
<url>https://mvn.lumine.io/repository/maven-public/</url>
</repository>
</repositories> </repositories>
<dependencies> <dependencies>
@ -112,14 +122,14 @@
<dependency> <dependency>
<groupId>com.songoda</groupId> <groupId>com.songoda</groupId>
<artifactId>SongodaCore</artifactId> <artifactId>SongodaCore</artifactId>
<version>2.6.17-SNAPSHOT</version> <version>2.6.18</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.bgsoftware</groupId> <groupId>com.bgsoftware</groupId>
<artifactId>wildstacker</artifactId> <artifactId>WildStackerAPI</artifactId>
<version>3.5.1</version> <version>3.8.1</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
@ -139,8 +149,8 @@
<dependency> <dependency>
<groupId>io.lumine</groupId> <groupId>io.lumine</groupId>
<artifactId>MythicMobs</artifactId> <artifactId>Mythic-Dist</artifactId>
<version>4.10.1</version> <version>5.2.0</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -27,6 +27,7 @@ import com.songoda.ultimatestacker.database.migrations._1_InitialMigration;
import com.songoda.ultimatestacker.database.migrations._2_EntityStacks; import com.songoda.ultimatestacker.database.migrations._2_EntityStacks;
import com.songoda.ultimatestacker.database.migrations._3_BlockStacks; import com.songoda.ultimatestacker.database.migrations._3_BlockStacks;
import com.songoda.ultimatestacker.database.migrations._4_DataPurge; import com.songoda.ultimatestacker.database.migrations._4_DataPurge;
import com.songoda.ultimatestacker.database.migrations._5_StackedEntitiesTableUpdate;
import com.songoda.ultimatestacker.hook.StackerHook; import com.songoda.ultimatestacker.hook.StackerHook;
import com.songoda.ultimatestacker.hook.hooks.JobsHook; import com.songoda.ultimatestacker.hook.hooks.JobsHook;
import com.songoda.ultimatestacker.listeners.*; import com.songoda.ultimatestacker.listeners.*;
@ -46,11 +47,13 @@ import com.songoda.ultimatestacker.stackable.entity.custom.CustomEntityManager;
import com.songoda.ultimatestacker.stackable.spawner.SpawnerStack; import com.songoda.ultimatestacker.stackable.spawner.SpawnerStack;
import com.songoda.ultimatestacker.stackable.spawner.SpawnerStackManager; import com.songoda.ultimatestacker.stackable.spawner.SpawnerStackManager;
import com.songoda.ultimatestacker.tasks.StackingTask; import com.songoda.ultimatestacker.tasks.StackingTask;
import com.songoda.ultimatestacker.utils.Async;
import com.songoda.ultimatestacker.utils.Methods; import com.songoda.ultimatestacker.utils.Methods;
import org.apache.commons.lang.WordUtils; import org.apache.commons.lang.WordUtils;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
import org.bukkit.entity.Item; import org.bukkit.entity.Item;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -62,6 +65,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Set; import java.util.Set;
public class UltimateStacker extends SongodaPlugin { public class UltimateStacker extends SongodaPlugin {
@ -102,13 +106,17 @@ public class UltimateStacker extends SongodaPlugin {
@Override @Override
public void onPluginDisable() { public void onPluginDisable() {
this.stackingTask.cancel();
this.stackingTask = null;
this.dataManager.bulkUpdateSpawners(this.spawnerStackManager.getStacks()); this.dataManager.bulkUpdateSpawners(this.spawnerStackManager.getStacks());
HologramManager.removeAllHolograms(); HologramManager.removeAllHolograms();
Async.shutdown();
} }
@Override @Override
public void onPluginEnable() { public void onPluginEnable() {
// Run Songoda Updater // Run Songoda Updater
Async.start();
SongodaCore.registerPlugin(this, 16, CompatibleMaterial.IRON_INGOT); SongodaCore.registerPlugin(this, 16, CompatibleMaterial.IRON_INGOT);
// Setup Config // Setup Config
Settings.setupConfig(); Settings.setupConfig();
@ -227,7 +235,8 @@ public class UltimateStacker extends SongodaPlugin {
new _1_InitialMigration(), new _1_InitialMigration(),
new _2_EntityStacks(), new _2_EntityStacks(),
new _3_BlockStacks(), new _3_BlockStacks(),
new _4_DataPurge()); new _4_DataPurge(),
new _5_StackedEntitiesTableUpdate());
this.dataMigrationManager.runMigrations(); this.dataMigrationManager.runMigrations();
} }
@ -391,9 +400,19 @@ public class UltimateStacker extends SongodaPlugin {
* @param location The location to spawn the item * @param location The location to spawn the item
*/ */
public static void spawnStackedItem(ItemStack item, int amount, Location location) { public static void spawnStackedItem(ItemStack item, int amount, Location location) {
location.getWorld().dropItem(location, item, dropped -> { if (item.getType() == Material.AIR) return;
updateItemAmount(dropped, amount); World world = location.getWorld();
}); if (world == null) return;
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_17)) {
world.dropItem(location, item, dropped -> {
if (dropped.getItemStack().getType() == Material.AIR) return;
updateItemMeta(dropped, item, amount);
});
} else {
Item dropped = world.dropItem(location, item);
if (dropped.getItemStack().getType() == Material.AIR) return;
updateItemMeta(dropped, item, amount);
}
} }
/** /**

View File

@ -3,6 +3,7 @@ package com.songoda.ultimatestacker.database;
import com.songoda.core.compatibility.CompatibleMaterial; import com.songoda.core.compatibility.CompatibleMaterial;
import com.songoda.core.database.DataManagerAbstract; import com.songoda.core.database.DataManagerAbstract;
import com.songoda.core.database.DatabaseConnector; import com.songoda.core.database.DatabaseConnector;
import com.songoda.core.database.DatabaseType;
import com.songoda.ultimatestacker.settings.Settings; import com.songoda.ultimatestacker.settings.Settings;
import com.songoda.ultimatestacker.stackable.block.BlockStack; import com.songoda.ultimatestacker.stackable.block.BlockStack;
import com.songoda.ultimatestacker.stackable.entity.ColdEntityStack; import com.songoda.ultimatestacker.stackable.entity.ColdEntityStack;
@ -68,7 +69,7 @@ public class DataManager extends DataManagerAbstract {
public void createSpawner(SpawnerStack spawnerStack) { public void createSpawner(SpawnerStack spawnerStack) {
this.runAsync(() -> { this.runAsync(() -> {
try (Connection connection = this.databaseConnector.getConnection()) { try (Connection connection = this.databaseConnector.getConnection()) {
String createSpawner = "INSERT INTO " + this.getTablePrefix() + "spawners (amount, world, x, y, z) VALUES (?, ?, ?, ?, ?)"; String createSpawner = "INSERT INTO " + getSyntax("OR REPLACE ", DatabaseType.SQLITE) + this.getTablePrefix() + "spawners (amount, world, x, y, z) VALUES (?, ?, ?, ?, ?)";
PreparedStatement statement = connection.prepareStatement(createSpawner); PreparedStatement statement = connection.prepareStatement(createSpawner);
statement.setInt(1, spawnerStack.getAmount()); statement.setInt(1, spawnerStack.getAmount());
@ -103,7 +104,7 @@ public class DataManager extends DataManagerAbstract {
public void createBlock(BlockStack blockStack) { public void createBlock(BlockStack blockStack) {
this.runAsync(() -> { this.runAsync(() -> {
try (Connection connection = this.databaseConnector.getConnection()) { try (Connection connection = this.databaseConnector.getConnection()) {
String createSpawner = "INSERT INTO " + this.getTablePrefix() + "blocks (amount, material, world, x, y, z) VALUES (?, ?, ?, ?, ?, ?)"; String createSpawner = "INSERT INTO " + getSyntax("OR REPLACE ", DatabaseType.SQLITE) + this.getTablePrefix() + "blocks (amount, material, world, x, y, z) VALUES (?, ?, ?, ?, ?, ?)";
PreparedStatement statement = connection.prepareStatement(createSpawner); PreparedStatement statement = connection.prepareStatement(createSpawner);
statement.setInt(1, blockStack.getAmount()); statement.setInt(1, blockStack.getAmount());
statement.setString(2, blockStack.getMaterial().name()); statement.setString(2, blockStack.getMaterial().name());
@ -124,7 +125,7 @@ public class DataManager extends DataManagerAbstract {
public void createHostEntity(ColdEntityStack stack) { public void createHostEntity(ColdEntityStack stack) {
this.runAsync(() -> { this.runAsync(() -> {
try (Connection connection = this.databaseConnector.getConnection()) { try (Connection connection = this.databaseConnector.getConnection()) {
String createSerializedEntity = "INSERT INTO " + this.getTablePrefix() + "host_entities (uuid, create_duplicates) VALUES (?, ?)"; String createSerializedEntity = "INSERT INTO " + getSyntax("OR REPLACE ", DatabaseType.SQLITE) + this.getTablePrefix() + "host_entities (uuid, create_duplicates) VALUES (?, ?)";
PreparedStatement statement = connection.prepareStatement(createSerializedEntity); PreparedStatement statement = connection.prepareStatement(createSerializedEntity);
if (stack == null || stack.getHostUniqueId() == null) return; if (stack == null || stack.getHostUniqueId() == null) return;
statement.setString(1, stack.getHostUniqueId().toString()); statement.setString(1, stack.getHostUniqueId().toString());
@ -141,12 +142,15 @@ public class DataManager extends DataManagerAbstract {
public void createStackedEntity(EntityStack hostStack, StackedEntity stackedEntity) { public void createStackedEntity(EntityStack hostStack, StackedEntity stackedEntity) {
this.runAsync(() -> { this.runAsync(() -> {
try (Connection connection = this.databaseConnector.getConnection()){ try (Connection connection = this.databaseConnector.getConnection()){
String createSerializedEntity = "INSERT INTO " + this.getTablePrefix() + "stacked_entities (uuid, host, serialized_entity) VALUES (?, ?, ?)"; String createSerializedEntity = "INSERT INTO " + getSyntax("OR REPLACE ", 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); PreparedStatement statement = connection.prepareStatement(createSerializedEntity);
if (hostStack.getHostUniqueId() == null) return; if (hostStack.getHostUniqueId() == null) return;
statement.setString(1, stackedEntity.getUniqueId().toString()); statement.setString(1, stackedEntity.getUniqueId().toString());
statement.setInt(2, hostStack.getId()); statement.setInt(2, hostStack.getId());
statement.setBytes(3, stackedEntity.getSerializedEntity()); statement.setBytes(3, stackedEntity.getSerializedEntity());
statement.setInt(4, hostStack.getId());
statement.setBytes(5, stackedEntity.getSerializedEntity());
statement.executeUpdate(); statement.executeUpdate();
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(); ex.printStackTrace();

View File

@ -32,7 +32,7 @@ public class _2_EntityStacks extends DataMigration {
statement.execute("CREATE TABLE " + tablePrefix + "stacked_entities (" + statement.execute("CREATE TABLE " + tablePrefix + "stacked_entities (" +
"uuid VARCHAR(36) PRIMARY KEY NOT NULL," + "uuid VARCHAR(36) PRIMARY KEY NOT NULL," +
"host INTEGER NOT NULL," + "host INTEGER NOT NULL," +
"serialized_entity VARBINARY(999) NOT NULL" + "serialized_entity VARBINARY(9999) NOT NULL" +
")"); ")");
} }
} }

View File

@ -0,0 +1,21 @@
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)");
}
}
}

View File

@ -0,0 +1,72 @@
package com.songoda.ultimatestacker.events;
import com.songoda.ultimatestacker.stackable.entity.EntityStack;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
/**
* Called when an entity is killed by a player which is stacked
*/
public class EntityStackKillEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private final EntityStack entityStack;
private final boolean instantKill;
public EntityStackKillEvent(EntityStack entityStack) {
this.entityStack = entityStack;
this.instantKill = false;
}
public EntityStackKillEvent(EntityStack entityStack, boolean instantKill) {
this.entityStack = entityStack;
this.instantKill = instantKill;
}
/**
* Get the host entity of the stack
*
* @return Entity
*/
public LivingEntity getEntity() {
return entityStack.getHostEntity();
}
/**
* Returns true if the entity was killed instantly
*
* @return true if the entity was killed instantly false otherwise
*/
public boolean isInstantKill() {
return instantKill;
}
/**
* Get the current size of the entity stack
*
* @return stack size
*/
public int getStackSize() {
return entityStack.getAmount();
}
/**
* Get the new size of the entity stack
*
* @return new stack size or 0 if instant killed
*/
public int getNewStackSize() {
return instantKill ? 0 : entityStack.getAmount() - 1;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
}

View File

@ -63,6 +63,9 @@ public class DeathListeners implements Listener {
if (event.getEntityType() == EntityType.PLAYER if (event.getEntityType() == EntityType.PLAYER
|| event.getEntityType() == EntityType.ARMOR_STAND) return; || event.getEntityType() == EntityType.ARMOR_STAND) return;
//Respect MythicMobs
if (plugin.getCustomEntityManager().isCustomEntity(entity)) return;
boolean custom = Settings.CUSTOM_DROPS.getBoolean(); boolean custom = Settings.CUSTOM_DROPS.getBoolean();
List<Drop> drops = custom ? plugin.getLootablesManager().getDrops(event.getEntity()) List<Drop> drops = custom ? plugin.getLootablesManager().getDrops(event.getEntity())
: event.getDrops().stream().map(Drop::new).collect(Collectors.toList()); : event.getDrops().stream().map(Drop::new).collect(Collectors.toList());
@ -78,11 +81,14 @@ public class DeathListeners implements Listener {
&& !entity.getWorld().getGameRuleValue(GameRule.DO_MOB_LOOT)) && !entity.getWorld().getGameRuleValue(GameRule.DO_MOB_LOOT))
drops.clear(); drops.clear();
if (plugin.getEntityStackManager().isStackedAndLoaded(event.getEntity())) if (plugin.getCustomEntityManager().getCustomEntity(entity) == null) {
plugin.getEntityStackManager().getStack(event.getEntity()) if (plugin.getEntityStackManager().isStackedAndLoaded(event.getEntity())) {
.onDeath(entity, drops, custom, event.getDroppedExp(), event); plugin.getEntityStackManager().getStack(event.getEntity()).onDeath(entity, drops, custom, event.getDroppedExp(), event);
else } else {
DropUtils.processStackedDrop(event.getEntity(), drops, event); DropUtils.processStackedDrop(event.getEntity(), drops, event);
}
}
finalItems.remove(entity.getUniqueId()); finalItems.remove(entity.getUniqueId());
} }

View File

@ -83,6 +83,11 @@ public class ItemListeners implements Listener {
return; //Compatibility with Shop instance: https://www.spigotmc.org/resources/shop-a-simple-intuitive-shop-instance.9628/ return; //Compatibility with Shop instance: https://www.spigotmc.org/resources/shop-a-simple-intuitive-shop-instance.9628/
} }
UltimateStacker.updateItemAmount(event.getEntity(), itemStack, itemStack.getAmount()); if (UltimateStacker.hasCustomAmount(event.getEntity())) {
UltimateStacker.updateItemAmount(event.getEntity(), itemStack, UltimateStacker.getActualItemAmount(event.getEntity()) + itemStack.getAmount());
} else {
UltimateStacker.updateItemAmount(event.getEntity(), itemStack, itemStack.getAmount());
}
} }
} }

View File

@ -11,6 +11,8 @@ import com.songoda.core.lootables.loot.LootManager;
import com.songoda.core.lootables.loot.Lootable; import com.songoda.core.lootables.loot.Lootable;
import com.songoda.ultimatestacker.UltimateStacker; import com.songoda.ultimatestacker.UltimateStacker;
import com.songoda.ultimatestacker.settings.Settings; import com.songoda.ultimatestacker.settings.Settings;
import com.songoda.ultimatestacker.stackable.entity.custom.CustomEntity;
import com.songoda.ultimatestacker.stackable.entity.custom.CustomEntityManager;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Ageable; import org.bukkit.entity.Ageable;
import org.bukkit.entity.Creeper; import org.bukkit.entity.Creeper;
@ -19,11 +21,15 @@ import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Projectile; import org.bukkit.entity.Projectile;
import org.bukkit.entity.Sheep; import org.bukkit.entity.Sheep;
import org.bukkit.entity.Zombie;
import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.inventory.ItemStack;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
public class LootablesManager { public class LootablesManager {
@ -41,7 +47,7 @@ public class LootablesManager {
public List<Drop> getDrops(LivingEntity entity) { public List<Drop> getDrops(LivingEntity entity) {
List<Drop> toDrop = new ArrayList<>(); List<Drop> toDrop = new ArrayList<>();
if (entity instanceof Ageable && !((Ageable) entity).isAdult() if (entity instanceof Ageable && !((Ageable) entity).isAdult() && !(entity instanceof Zombie)
|| !lootManager.getRegisteredLootables().containsKey(entity.getType().name())) return toDrop; || !lootManager.getRegisteredLootables().containsKey(entity.getType().name())) return toDrop;
Lootable lootable = lootManager.getRegisteredLootables().get(entity.getType().name()); Lootable lootable = lootManager.getRegisteredLootables().get(entity.getType().name());
@ -63,7 +69,7 @@ public class LootablesManager {
if (entity instanceof Sheep) { if (entity instanceof Sheep) {
modify = (Loot loot2) -> { modify = (Loot loot2) -> {
CompatibleMaterial material = loot2.getMaterial(); CompatibleMaterial material = loot2.getMaterial();
if (material.name().contains("WOOL") && ((Sheep) entity).getColor() != null) { if (material != null && material.name().contains("WOOL") && ((Sheep) entity).getColor() != null) {
if (((Sheep) entity).isSheared()) return null; if (((Sheep) entity).isSheared()) return null;
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)) if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13))
loot2.setMaterial(CompatibleMaterial.valueOf(((Sheep) entity).getColor() + "_WOOL")); loot2.setMaterial(CompatibleMaterial.valueOf(((Sheep) entity).getColor() + "_WOOL"));
@ -95,6 +101,77 @@ public class LootablesManager {
looting); looting);
} }
public List<Drop> getDrops(LivingEntity entity, int times) {
return getDrops(entity, times, 3);
}
public List<Drop> getDrops(LivingEntity entity, int times, int attempts) {
attempts--;
List<Drop> toDrop = new ArrayList<>();
if (entity instanceof Ageable && !((Ageable) entity).isAdult() && !(entity instanceof Zombie)
|| !lootManager.getRegisteredLootables().containsKey(entity.getType().name())) return toDrop;
Lootable lootable = lootManager.getRegisteredLootables().get(entity.getType().name());
int looting = entity.getKiller() != null
&& entity.getKiller().getItemInHand().containsEnchantment(Enchantment.LOOT_BONUS_MOBS)
? entity.getKiller().getItemInHand().getEnchantmentLevel(Enchantment.LOOT_BONUS_MOBS)
: 0;
double extraChance = looting / (looting + 1.0);
Random random = new Random();
boolean isCharged = entity instanceof Creeper && ((Creeper) entity).isPowered();
//Run main loot
for (Loot loot : lootable.getRegisteredLoot().stream().filter(loot -> loot.getMaterial() != null).collect(Collectors.toList())) {
if (loot.isRequireCharged() && !isCharged) continue;
if (loot.getOnlyDropFor().size() != 0 && loot.getOnlyDropFor().stream().noneMatch(type -> entity.getKiller() != null && type == entity.getKiller().getType())) continue;
int finalLooting = loot.isAllowLootingEnchant() ? looting : 0;
int max = (int) (((loot.getMax() + finalLooting) * times) * (loot.getChance()/100));
int min = (int) ((loot.getMin()) * times * (loot.getChance()/100));
int amount = random.nextInt((max - min) + 1) + min;
if (amount > 0) {
ItemStack item = entity.getFireTicks() > 0
? loot.getBurnedMaterial() != null ? loot.getBurnedMaterial().getItem() : loot.getMaterial().getItem()
: loot.getMaterial().getItem().clone();
item.setAmount(amount);
toDrop.add(new Drop(item));
}
}
//Run child loot
for (Loot loot : lootable.getRegisteredLoot().stream().filter(loot -> loot.getMaterial() == null).collect(Collectors.toList())) {
for (Loot child : loot.getChildLoot()) {
if (child.isRequireCharged() && !isCharged) continue;
if (loot.getOnlyDropFor().size() != 0 && loot.getOnlyDropFor().stream().noneMatch(type -> entity.getKiller() != null && type == entity.getKiller().getType())) continue;
int finalLooting = child.isAllowLootingEnchant() ? looting : 0;
int max = (int) (((loot.getMax() + finalLooting) * times * (loot.getChance()/100)));
int min = (int) ((loot.getMin()) * times * (loot.getChance()/100));
min = (int) (min - min*0.90);
int amount = random.nextInt((max - min) + 1) + min;
if (amount > 0) {
ItemStack item = entity.getFireTicks() > 0
? child.getBurnedMaterial() != null ? child.getBurnedMaterial().getItem() : child.getMaterial().getItem()
: child.getMaterial().getItem().clone();
item.setAmount(amount);
toDrop.add(new Drop(item));
}
}
}
if (toDrop.isEmpty() && attempts > 0) {
return getDrops(entity, times, attempts);
}
return toDrop;
}
public void createDefaultLootables() { public void createDefaultLootables() {
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_17)) { if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_17)) {
// Add Glow Squid. // Add Glow Squid.

View File

@ -58,7 +58,7 @@ public class ColdEntityStack implements Stackable {
this.stackedEntities.addAll(stackedEntities); this.stackedEntities.addAll(stackedEntities);
} }
public StackedEntity addEntityToStackSilently(StackedEntity stackedEntity) { public synchronized StackedEntity addEntityToStackSilently(StackedEntity stackedEntity) {
if (stackedEntity == null) return null; if (stackedEntity == null) return null;
stackedEntities.push(stackedEntity); stackedEntities.push(stackedEntity);
return stackedEntity; return stackedEntity;
@ -140,17 +140,15 @@ public class ColdEntityStack implements Stackable {
return getStackedEntity(entity, false); return getStackedEntity(entity, false);
} }
protected StackedEntity getStackedEntity(Entity entity, boolean newUUID) { protected synchronized StackedEntity getStackedEntity(Entity entity, boolean newUUID) {
if (entity == null) return null;
UUID uuid = entity.getUniqueId(); UUID uuid = entity.getUniqueId();
NBTEntity nbtEntity = NmsManager.getNbt().of(entity); NBTEntity nbtEntity = NmsManager.getNbt().of(entity);
if (newUUID) {
uuid = UUID.randomUUID();
nbtEntity.set("UUID", uuid);
}
CustomEntity customEntity = plugin.getCustomEntityManager().getCustomEntity(entity); CustomEntity customEntity = plugin.getCustomEntityManager().getCustomEntity(entity);
if (customEntity != null) if (customEntity != null)
nbtEntity.set(customEntity.getPluginName() + "_UltimateStacker", customEntity.getNBTIdentifier(entity)); nbtEntity.set(customEntity.getPluginName() + "_UltimateStacker", customEntity.getNBTIdentifier(entity));
return new StackedEntity(uuid, nbtEntity.serialize("Attributes")); return new StackedEntity(uuid, nbtEntity.serialize());
} }
public int getId() { public int getId() {

View File

@ -1,15 +1,17 @@
package com.songoda.ultimatestacker.stackable.entity; package com.songoda.ultimatestacker.stackable.entity;
import com.google.common.base.Throwables;
import com.songoda.core.compatibility.ServerVersion; import com.songoda.core.compatibility.ServerVersion;
import com.songoda.core.lootables.loot.Drop; import com.songoda.core.lootables.loot.Drop;
import com.songoda.core.lootables.loot.DropUtils; import com.songoda.core.lootables.loot.DropUtils;
import com.songoda.ultimatestacker.UltimateStacker; import com.songoda.ultimatestacker.UltimateStacker;
import com.songoda.ultimatestacker.events.EntityStackKillEvent;
import com.songoda.ultimatestacker.settings.Settings; import com.songoda.ultimatestacker.settings.Settings;
import com.songoda.ultimatestacker.utils.Async;
import com.songoda.ultimatestacker.utils.Methods; import com.songoda.ultimatestacker.utils.Methods;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.ExperienceOrb; import org.bukkit.entity.ExperienceOrb;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityDamageEvent;
@ -58,23 +60,35 @@ public class EntityStack extends ColdEntityStack {
} }
public void updateStack() { public void updateStack() {
if (createDuplicates != 0) { Async.run(() -> {
List<StackedEntity> stackedEntities = new ArrayList<>(); if (createDuplicates != 0) {
for (int i = 0; i < createDuplicates; i++) List<StackedEntity> stackedEntities = new ArrayList<>();
stackedEntities.add(addEntityToStackSilently(getStackedEntity(hostEntity, true))); try {
plugin.getDataManager().createStackedEntities(this, stackedEntities); 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; 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;
} }
if (!Settings.ENTITY_NAMETAGS.getBoolean()) return; hostEntity.setCustomNameVisible(!Settings.HOLOGRAMS_ON_LOOK_ENTITY.getBoolean());
hostEntity.setCustomName(Methods.compileEntityName(hostEntity, getAmount()));
Bukkit.getScheduler().scheduleSyncDelayedTask(UltimateStacker.getInstance(), () -> {
if (hostEntity == null) return;
hostEntity.setCustomNameVisible(!Settings.HOLOGRAMS_ON_LOOK_ENTITY.getBoolean());
hostEntity.setCustomName(Methods.compileEntityName(hostEntity, getAmount()));
}, hostEntity == null ? 1L : 0L);
} }
public LivingEntity getHostEntity() { public LivingEntity getHostEntity() {
@ -97,20 +111,6 @@ public class EntityStack extends ColdEntityStack {
private void handleWholeStackDeath(LivingEntity killed, List<Drop> drops, boolean custom, int droppedExp, EntityDeathEvent event) { private void handleWholeStackDeath(LivingEntity killed, List<Drop> drops, boolean custom, int droppedExp, EntityDeathEvent event) {
plugin.getDataManager().deleteHost(this); plugin.getDataManager().deleteHost(this);
List<Drop> preStackedDrops = new ArrayList<>();
for (int i = 1; i < getAmount(); i++) {
if (i == 1) {
drops.removeIf(it -> it.getItemStack() != null
&& it.getItemStack().isSimilar(killed.getEquipment().getItemInHand()));
for (ItemStack item : killed.getEquipment().getArmorContents()) {
drops.removeIf(it -> it.getItemStack() != null && it.getItemStack().isSimilar(item));
}
}
if (custom)
drops = plugin.getLootablesManager().getDrops(killed);
preStackedDrops.addAll(drops);
}
// In versions 1.14 and below experience is not dropping. Because of this we are doing this ourselves. // In versions 1.14 and below experience is not dropping. Because of this we are doing this ourselves.
if (ServerVersion.isServerVersionAtOrBelow(ServerVersion.V1_14)) { if (ServerVersion.isServerVersionAtOrBelow(ServerVersion.V1_14)) {
Location killedLocation = killed.getLocation(); Location killedLocation = killed.getLocation();
@ -120,19 +120,35 @@ public class EntityStack extends ColdEntityStack {
event.setDroppedExp(droppedExp * getAmount()); event.setDroppedExp(droppedExp * getAmount());
} }
DropUtils.processStackedDrop(killed, preStackedDrops, event);
if (plugin.getCustomEntityManager().getCustomEntity(killed) == null) {
Async.run(() -> {
drops.removeIf(it -> it.getItemStack() != null
&& it.getItemStack().isSimilar(killed.getEquipment().getItemInHand()));
for (ItemStack item : killed.getEquipment().getArmorContents()) {
drops.removeIf(it -> it.getItemStack() != null && it.getItemStack().isSimilar(item));
}
DropUtils.processStackedDrop(killed, plugin.getLootablesManager().getDrops(killed, getAmount()), event);
});
}
event.getDrops().clear();
plugin.getEntityStackManager().removeStack(event.getEntity());
if (killed.getKiller() == null) return; if (killed.getKiller() == null) return;
plugin.addExp(killed.getKiller(), this); plugin.addExp(killed.getKiller(), this);
plugin.getEntityStackManager().removeStack(event.getEntity());
} }
private void handleSingleStackDeath(LivingEntity killed, List<Drop> drops, int droppedExp, EntityDeathEvent event) { private void handleSingleStackDeath(LivingEntity killed, List<Drop> drops, int droppedExp, EntityDeathEvent event) {
EntityStackManager stackManager = plugin.getEntityStackManager(); EntityStackManager stackManager = plugin.getEntityStackManager();
Bukkit.getPluginManager().callEvent(new EntityStackKillEvent(this, false));
Vector velocity = killed.getVelocity().clone(); Vector velocity = killed.getVelocity().clone();
killed.remove(); killed.remove();
LivingEntity newEntity = takeOneAndSpawnEntity(killed.getLocation()); LivingEntity newEntity = takeOneAndSpawnEntity(killed.getLocation());
if (newEntity == null) {
return;
}
// In versions 1.14 and below experience is not dropping. Because of this we are doing this ourselves. // In versions 1.14 and below experience is not dropping. Because of this we are doing this ourselves.
if (ServerVersion.isServerVersionAtOrBelow(ServerVersion.V1_14)) { if (ServerVersion.isServerVersionAtOrBelow(ServerVersion.V1_14)) {
@ -140,16 +156,18 @@ public class EntityStack extends ColdEntityStack {
if (droppedExp > 0) if (droppedExp > 0)
killedLocation.getWorld().spawn(killedLocation, ExperienceOrb.class).setExperience(droppedExp); killedLocation.getWorld().spawn(killedLocation, ExperienceOrb.class).setExperience(droppedExp);
} }
if (plugin.getCustomEntityManager().getCustomEntity(killed) == null) {
DropUtils.processStackedDrop(killed, drops, event); DropUtils.processStackedDrop(killed, drops, event);
}
newEntity.setVelocity(velocity); newEntity.setVelocity(velocity);
stackManager.updateStack(killed, newEntity); stackManager.updateStack(killed, newEntity);
updateStack(); updateStack();
if (stackedEntities.isEmpty()) if (stackedEntities.isEmpty()) {
destroy(); destroy();
}
} }
public void onDeath(LivingEntity killed, List<Drop> drops, boolean custom, int droppedExp, EntityDeathEvent event) { public void onDeath(LivingEntity killed, List<Drop> drops, boolean custom, int droppedExp, EntityDeathEvent event) {
@ -200,8 +218,10 @@ public class EntityStack extends ColdEntityStack {
if (full) if (full)
plugin.getEntityStackManager().removeStack(this.hostUniqueId); plugin.getEntityStackManager().removeStack(this.hostUniqueId);
if (hostEntity != null) { if (hostEntity != null) {
hostEntity.setCustomNameVisible(false); try {
hostEntity.setCustomName(null); hostEntity.setCustomNameVisible(false);
hostEntity.setCustomName(null);
} catch (NullPointerException ignored) {}
} }
hostEntity = null; hostEntity = null;
hostUniqueId = null; hostUniqueId = null;

View File

@ -24,4 +24,6 @@ public abstract class CustomEntity {
public abstract String getNBTIdentifier(Entity entity); public abstract String getNBTIdentifier(Entity entity);
public abstract LivingEntity spawnFromIdentifier(String string, Location location); public abstract LivingEntity spawnFromIdentifier(String string, Location location);
public abstract boolean isCustomEntity(Entity entity);
} }

View File

@ -29,7 +29,7 @@ public class CustomEntityManager {
public CustomEntity getCustomEntity(Entity entity) { public CustomEntity getCustomEntity(Entity entity) {
for (CustomEntity customEntity : registeredCustomEntities) { for (CustomEntity customEntity : registeredCustomEntities) {
if (customEntity.isMatchingType(entity)) { if (customEntity.isMatchingType(entity) && customEntity.isCustomEntity(entity)) {
if (Settings.BLACKLISTED_CUSTOM_ENTITIES.getStringList() if (Settings.BLACKLISTED_CUSTOM_ENTITIES.getStringList()
.contains((customEntity.getPluginName() + "_" + customEntity.getNBTIdentifier(entity)).toLowerCase())) .contains((customEntity.getPluginName() + "_" + customEntity.getNBTIdentifier(entity)).toLowerCase()))
continue; continue;
@ -42,4 +42,8 @@ public class CustomEntityManager {
public List<CustomEntity> getRegisteredCustomEntities() { public List<CustomEntity> getRegisteredCustomEntities() {
return Collections.unmodifiableList(registeredCustomEntities); return Collections.unmodifiableList(registeredCustomEntities);
} }
public boolean isCustomEntity(Entity entity) {
return getCustomEntity(entity) != null && getCustomEntity(entity).isCustomEntity(entity);
}
} }

View File

@ -1,13 +1,14 @@
package com.songoda.ultimatestacker.stackable.entity.custom.entities; package com.songoda.ultimatestacker.stackable.entity.custom.entities;
import com.songoda.ultimatestacker.stackable.entity.custom.CustomEntity; import com.songoda.ultimatestacker.stackable.entity.custom.CustomEntity;
import io.lumine.xikage.mythicmobs.MythicMobs; import io.lumine.mythic.api.mobs.MobManager;
import io.lumine.xikage.mythicmobs.mobs.ActiveMob; import io.lumine.mythic.bukkit.BukkitAdapter;
import io.lumine.xikage.mythicmobs.mobs.MobManager; import io.lumine.mythic.bukkit.MythicBukkit;
import io.lumine.xikage.mythicmobs.mobs.MythicMob; import io.lumine.mythic.core.mobs.ActiveMob;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
public class MythicMobsCustomEntity extends CustomEntity { public class MythicMobsCustomEntity extends CustomEntity {
@ -23,17 +24,21 @@ public class MythicMobsCustomEntity extends CustomEntity {
@Override @Override
public boolean isMatchingType(Entity entity) { public boolean isMatchingType(Entity entity) {
return getMobManager().isActiveMob(entity.getUniqueId()); return getMobManager().getActiveMobs().stream().anyMatch(activeMob -> activeMob.getEntity().getBukkitEntity().getType().equals(entity.getType()));
} }
@Override @Override
public String getDisplayName(Entity entity) { public String getDisplayName(Entity entity) {
return getMobManager().getMythicMobInstance(entity).getType().getDisplayName().toString(); return getMobManager().getActiveMobs().stream()
.filter(activeMob -> activeMob.getEntity().getBukkitEntity().getUniqueId().equals(entity.getUniqueId()))
.findFirst()
.map(ActiveMob::getMobType)
.orElse(null);
} }
@Override @Override
public boolean isSimilar(LivingEntity original, LivingEntity entity) { public boolean isSimilar(LivingEntity original, LivingEntity entity) {
if (!isMatchingType(original) || !isMatchingType(entity)) return false; if (!isMatchingType(original) || !isMatchingType(entity) || getMob(entity) == null) return false;
return getMob(original).getType().equals(getMob(entity).getType()); return getMob(original).getType().equals(getMob(entity).getType());
} }
@ -44,16 +49,22 @@ public class MythicMobsCustomEntity extends CustomEntity {
@Override @Override
public LivingEntity spawnFromIdentifier(String string, Location location) { public LivingEntity spawnFromIdentifier(String string, Location location) {
if (getMobManager().getMobTypes().stream().map(MythicMob::getInternalName).noneMatch(t -> t.equals(string))) if (getMobManager().getMythicMob(string).isPresent()) {
return null; return null;
return (LivingEntity)getMobManager().spawnMob(string, location).getEntity().getBukkitEntity(); }
return (LivingEntity)getMobManager().getMythicMob(string).get().spawn(BukkitAdapter.adapt(location), 1).getEntity().getBukkitEntity();
}
@Override
public boolean isCustomEntity(Entity entity) {
return getMob(entity) != null;
} }
private ActiveMob getMob(Entity entity) { private ActiveMob getMob(Entity entity) {
return getMobManager().getMythicMobInstance(entity); return MythicBukkit.inst().getMobManager().getMythicMobInstance(entity);
} }
private MobManager getMobManager() { private MobManager getMobManager() {
return ((MythicMobs) plugin).getMobManager(); return ((MythicBukkit) plugin).getMobManager();
} }
} }

View File

@ -11,6 +11,7 @@ import com.songoda.ultimatestacker.stackable.entity.EntityStack;
import com.songoda.ultimatestacker.stackable.entity.EntityStackManager; import com.songoda.ultimatestacker.stackable.entity.EntityStackManager;
import com.songoda.ultimatestacker.stackable.entity.StackedEntity; import com.songoda.ultimatestacker.stackable.entity.StackedEntity;
import com.songoda.ultimatestacker.stackable.entity.custom.CustomEntity; import com.songoda.ultimatestacker.stackable.entity.custom.CustomEntity;
import com.songoda.ultimatestacker.utils.Async;
import com.songoda.ultimatestacker.utils.CachedChunk; import com.songoda.ultimatestacker.utils.CachedChunk;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;

View File

@ -0,0 +1,21 @@
package com.songoda.ultimatestacker.utils;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Async {
private static ExecutorService executor;
public static void start() {
executor = Executors.newFixedThreadPool(5);
}
public static void run(Runnable runnable) {
executor.execute(runnable);
}
public static void shutdown() {
executor.shutdown();
}
}

View File

@ -118,8 +118,9 @@ public class Methods {
String displayName = TextUtils.formatText(UltimateStacker.getInstance().getMobFile().getString("Mobs." + entity.getType().name() + ".Display Name")); String displayName = TextUtils.formatText(UltimateStacker.getInstance().getMobFile().getString("Mobs." + entity.getType().name() + ".Display Name"));
CustomEntity customEntity = UltimateStacker.getInstance().getCustomEntityManager().getCustomEntity(entity); CustomEntity customEntity = UltimateStacker.getInstance().getCustomEntityManager().getCustomEntity(entity);
if (customEntity != null) if (customEntity != null) {
displayName = customEntity.getDisplayName(entity); displayName = customEntity.getDisplayName(entity);
}
nameFormat = nameFormat.replace("{TYPE}", displayName); nameFormat = nameFormat.replace("{TYPE}", displayName);
nameFormat = nameFormat.replace("{AMT}", Integer.toString(amount)); nameFormat = nameFormat.replace("{AMT}", Integer.toString(amount));