Fixed soulbound when disabling 'keep-soulbound-on-death'

This commit is contained in:
Jules 2023-04-15 16:58:36 +02:00
parent ebe041d8d5
commit d1f2af29c3
4 changed files with 110 additions and 125 deletions

View File

@ -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())

View File

@ -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<ItemStack> 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<UUID, DeathItemsHandler> 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.
* <p>
* 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<DeathItemsHandler> getActive() {
return INFO.values();
}
}

View File

@ -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<ItemStack> 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<UUID, SoulboundInfo> 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<SoulboundInfo> getAbandonnedInfo() {
return INFO.values();
}
}

View File

@ -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<Player, List<ItemStack>> 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<ItemStack> iterator = event.getDrops().iterator();
final Iterator<ItemStack> 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