diff --git a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/MMOItems.java b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/MMOItems.java index 5823bc53..3ae11891 100644 --- a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/MMOItems.java +++ b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/MMOItems.java @@ -5,7 +5,7 @@ import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackMessage; import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider; import io.lumine.mythic.lib.version.SpigotPlugin; import net.Indyuce.mmoitems.api.ItemTier; -import net.Indyuce.mmoitems.api.SoulboundInfo; +import net.Indyuce.mmoitems.api.DeathItemsHandler; import net.Indyuce.mmoitems.api.Type; import net.Indyuce.mmoitems.api.crafting.MMOItemUIFilter; import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem; @@ -263,8 +263,8 @@ public class MMOItems extends JavaPlugin { // Save player data PlayerData.getLoaded().forEach(data -> data.save(false)); - // Drop abandonned soulbound items - SoulboundInfo.getAbandonnedInfo().forEach(SoulboundInfo::dropItems); + // Drop abandoned items + DeathItemsHandler.getActive().forEach(info -> info.giveItems(true)); // Close inventories for (Player player : Bukkit.getOnlinePlayers()) diff --git a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/DeathItemsHandler.java b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/DeathItemsHandler.java new file mode 100644 index 00000000..8bd5b750 --- /dev/null +++ b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/DeathItemsHandler.java @@ -0,0 +1,87 @@ +package net.Indyuce.mmoitems.api; + +import java.util.*; + +import net.Indyuce.mmoitems.MMOItems; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class DeathItemsHandler { + + /** + * Items that need to be given to the player whenever he respawns. + */ + private final List items = new ArrayList<>(); + + /** + * If the player leaves the server before removing, the cached items will be + * lost. the plugin saves the last location of the player to drop the items + * when the server shuts down this way they are 'saved' + */ + private final Player player; + + /** + * Used to store which items must be given back to which player + */ + private static final Map INFO = new WeakHashMap<>(); + + /** + * Instanced when a player dies if some Soulbound items must be kept in the + * player's inventory and need to be cached before the player respawns. + *

+ * If the player leaves the server leaving one object of this type in server + * RAM, the cached items need to be dropped if the server closes before the + * player respawns again. + */ + public DeathItemsHandler(@NotNull Player player) { + this.player = player; + } + + public void registerItem(@NotNull ItemStack item) { + items.add(item); + } + + public void registerIfNecessary() { + if (!items.isEmpty()) INFO.put(player.getUniqueId(), this); + } + + /** + * @param forceDrop Should the items all drop on the ground + */ + public void giveItems(boolean forceDrop) { + + // Drop all items on the ground + if (forceDrop) for (ItemStack drop : items) + player.getWorld().dropItem(player.getLocation(), drop); + + // First try to add them to inventory + else { + final ItemStack[] toArray = this.items.toArray(new ItemStack[0]); + for (ItemStack drop : player.getInventory().addItem(toArray).values()) + player.getWorld().dropItem(player.getLocation(), drop); + } + } + + /** + * Tries to add the items to the player's inventory, or drops them + * on the ground if the player's inventory is full. In the meantime, + * the player's item info is removed from the map. + * + * @param player Target player respawning + */ + public static void readAndRemove(@NotNull Player player) { + final @Nullable DeathItemsHandler handler = INFO.remove(player.getUniqueId()); + if (handler != null) Bukkit.getScheduler().runTaskLater(MMOItems.plugin, () -> handler.giveItems(false), 10); + } + + /** + * @return Soulbound info of players who have not clicked the respawn button + * and yet have items cached in server RAM + */ + public static Collection getActive() { + return INFO.values(); + } +} diff --git a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/SoulboundInfo.java b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/SoulboundInfo.java deleted file mode 100644 index 60b3263e..00000000 --- a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/SoulboundInfo.java +++ /dev/null @@ -1,83 +0,0 @@ -package net.Indyuce.mmoitems.api; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -public class SoulboundInfo { - - /* - * Items that need to be given to the player whenever he respawns. - */ - private final List items = new ArrayList<>(); - - /* - * If the player leaves the server before removing, the cached items will be - * lost. the plugin saves the last location of the player to drop the items - * when the server shuts down this way they are 'saved' - */ - private final Location loc; - private final Player player; - - /** - * Used to store which items must be given back to which player - */ - private static final Map INFO = new HashMap<>(); - - /** - * Instanced when a player dies if some souljbound items must be kept in the - * player's inventory and need to be cached before the player respawns. - * - * If the player leaves the server leaving one object of this type in server - * RAM, the cached items need to be dropped if the server closes before the - * player respawns again - */ - public SoulboundInfo(Player player) { - this.player = player; - loc = player.getLocation().clone(); - } - - public void add(ItemStack item) { - items.add(item); - } - - public boolean hasItems() { - return !items.isEmpty(); - } - - public void setup() { - INFO.put(player.getUniqueId(), this); - } - - public void giveItems() { - for (ItemStack item : items) - for (ItemStack drop : player.getInventory().addItem(item).values()) - player.getWorld().dropItem(player.getLocation(), drop); - } - - public void dropItems() { - items.forEach(item -> loc.getWorld().dropItem(loc, item)); - } - - public static void read(Player player) { - if (INFO.containsKey(player.getUniqueId())) { - INFO.get(player.getUniqueId()).giveItems(); - INFO.remove(player.getUniqueId()); - } - } - - /** - * @return Soulbound info of players who have not clicked the respawn button - * and yet have items cached in server RAM - */ - public static Collection getAbandonnedInfo() { - return INFO.values(); - } -} diff --git a/MMOItems-Dist/src/main/java/net/Indyuce/mmoitems/listener/PlayerListener.java b/MMOItems-Dist/src/main/java/net/Indyuce/mmoitems/listener/PlayerListener.java index 1c3ea2a7..92d0fb76 100644 --- a/MMOItems-Dist/src/main/java/net/Indyuce/mmoitems/listener/PlayerListener.java +++ b/MMOItems-Dist/src/main/java/net/Indyuce/mmoitems/listener/PlayerListener.java @@ -5,7 +5,7 @@ import io.lumine.mythic.lib.api.event.armorequip.ArmorEquipEvent; import io.lumine.mythic.lib.api.item.NBTItem; import io.lumine.mythic.lib.api.player.EquipmentSlot; import net.Indyuce.mmoitems.MMOItems; -import net.Indyuce.mmoitems.api.SoulboundInfo; +import net.Indyuce.mmoitems.api.DeathItemsHandler; import net.Indyuce.mmoitems.api.Type; import net.Indyuce.mmoitems.api.interaction.util.InteractItem; import net.Indyuce.mmoitems.api.interaction.weapon.Weapon; @@ -28,12 +28,12 @@ import org.jetbrains.annotations.NotNull; import java.util.*; public class PlayerListener implements Listener { - private final Map> deathItems = new HashMap<>(); /** - * Careful, MMOCore and MMOMana runs on LOWEST + * MythicLib runs of LOWEST + * MMOCore and MMOInventory runs on LOW */ - @EventHandler(priority = EventPriority.LOW) + @EventHandler(priority = EventPriority.NORMAL) public void loadPlayerData(PlayerJoinEvent event) { MMOItems.plugin.getRecipes().refreshRecipeBook(event.getPlayer()); PlayerData.load(event.getPlayer()); @@ -60,59 +60,40 @@ public class PlayerListener implements Listener { /** * Prevent players from dropping items which are bound to them with a - * soulbound. Items are cached inside a map waiting for the player to + * Soulbound. Items are cached inside a map waiting for the player to * respawn. If he does not respawn the items are dropped on the ground, this * way there don't get lost */ @EventHandler(priority = EventPriority.HIGH) - public void onDeathForSoulbound(PlayerDeathEvent event) { - if (event.getKeepInventory() || !MMOItems.plugin.getLanguage().keepSoulboundOnDeath) - return; + public void keepItemsOnDeath(PlayerDeathEvent event) { + if (event.getKeepInventory()) return; - Player player = event.getEntity(); - SoulboundInfo soulboundInfo = new SoulboundInfo(player); + final Player player = event.getEntity(); + final DeathItemsHandler soulboundInfo = new DeathItemsHandler(player); - Iterator iterator = event.getDrops().iterator(); + final Iterator iterator = event.getDrops().iterator(); while (iterator.hasNext()) { - ItemStack item = iterator.next(); - NBTItem nbt = NBTItem.get(item); - - if (nbt.hasTag("MMOITEMS_DISABLE_DEATH_DROP") && nbt.getBoolean("MMOITEMS_DISABLE_DEATH_DROP")) { - iterator.remove(); - if (!deathItems.containsKey(player)) - deathItems.put(player, new ArrayList<>()); - - deathItems.get(player).add(item); - } + final ItemStack item = iterator.next(); + final NBTItem nbt = NBTItem.get(item); /* - * not a perfect check but it's very sufficient and so we avoid + * Not a perfect check but it's very sufficient and so we avoid * using a JsonParser followed by map checkups in the SoulboundData * constructor */ - else if (nbt.hasTag("MMOITEMS_SOULBOUND") && nbt.getString("MMOITEMS_SOULBOUND").contains(player.getUniqueId().toString())) { + if ((MMOItems.plugin.getLanguage().keepSoulboundOnDeath && nbt.getBoolean("MMOITEMS_DISABLE_DEATH_DROP")) + || (nbt.hasTag("MMOITEMS_SOULBOUND") && nbt.getString("MMOITEMS_SOULBOUND").contains(player.getUniqueId().toString()))) { iterator.remove(); - soulboundInfo.add(item); + soulboundInfo.registerItem(item); } } - if (soulboundInfo.hasItems()) - soulboundInfo.setup(); + soulboundInfo.registerIfNecessary(); } @EventHandler public void onRespawn(PlayerRespawnEvent event) { - Player player = event.getPlayer(); - - if (MMOItems.plugin.getLanguage().keepSoulboundOnDeath) - SoulboundInfo.read(player); - - if (deathItems.containsKey(player)) { - Bukkit.getScheduler().runTaskLater(MMOItems.plugin, () -> { - player.getInventory().addItem(deathItems.get(player).toArray(new ItemStack[0])); - deathItems.remove(player); - }, 10); - } + DeathItemsHandler.readAndRemove(event.getPlayer()); } @EventHandler @@ -161,7 +142,7 @@ public class PlayerListener implements Listener { * player cast abilities or attacks with not the correct stats * * @deprecated This does cost some performance and that update - * method NEEDS some improvement in the future + * method NEEDS some improvement in the future */ @Deprecated @EventHandler @@ -175,7 +156,7 @@ public class PlayerListener implements Listener { * player cast abilities or attacks with not the correct stats * * @deprecated This does cost some performance and that update - * method NEEDS some improvement in the future + * method NEEDS some improvement in the future */ @Deprecated @EventHandler