mirror of
https://gitlab.com/phoenix-dvpmt/mmoitems.git
synced 2025-01-20 09:11:21 +01:00
Decent version
I finally got my brain on that and coded something cleaner and finally working (sets are not working yet tho)
This commit is contained in:
parent
68a047fda6
commit
12219601a1
@ -40,6 +40,7 @@ import net.Indyuce.mmoitems.comp.rpg.McMMOHook;
|
||||
import net.Indyuce.mmoitems.comp.rpg.RPGHandler;
|
||||
import net.Indyuce.mmoitems.gui.PluginInventory;
|
||||
import net.Indyuce.mmoitems.gui.edition.recipe.RecipeBrowserGUI;
|
||||
import net.Indyuce.mmoitems.listener.InventoryListener;
|
||||
import net.Indyuce.mmoitems.manager.*;
|
||||
import net.Indyuce.mmoitems.util.PluginUtils;
|
||||
import org.apache.commons.lang.Validate;
|
||||
@ -187,6 +188,7 @@ public class MMOItems extends JavaPlugin {
|
||||
|
||||
Bukkit.getPluginManager().registerEvents(entityManager, this);
|
||||
Bukkit.getPluginManager().registerEvents(dropTableManager, this);
|
||||
Bukkit.getPluginManager().registerEvents(new InventoryListener(), this);
|
||||
|
||||
// Load Dist module
|
||||
// Load MMOCore-Bukkit module
|
||||
@ -203,7 +205,7 @@ public class MMOItems extends JavaPlugin {
|
||||
*/
|
||||
Bukkit.getScheduler().runTaskTimer(this, () -> {
|
||||
for (Player player : Bukkit.getOnlinePlayers())
|
||||
PlayerData.get(player).getInventory().updateCheck();
|
||||
PlayerData.get(player).getInventory().update();
|
||||
}, 100, getConfig().getInt("inventory-update-delay"));
|
||||
|
||||
PluginUtils.isDependencyPresent("mcMMO", unused -> Bukkit.getPluginManager().registerEvents(new McMMONonRPGHook(), this));
|
||||
|
@ -173,7 +173,7 @@ public class PlayerData {
|
||||
* Very important, clear particle data AFTER canceling the runnable
|
||||
* otherwise it cannot cancel and the runnable keeps going (severe)
|
||||
*/
|
||||
inventory.content().clear();
|
||||
//inventory.content().clear();
|
||||
permanentEffects.clear();
|
||||
cancelRunnables();
|
||||
mmoData.getPassiveSkillMap().removeModifiers("MMOItemsItem");
|
||||
@ -436,6 +436,11 @@ public class PlayerData {
|
||||
overridingItemParticles = null;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public void setOverridingItemParticles(@NotNull ParticleRunnable overridingItemParticles) {
|
||||
this.overridingItemParticles = overridingItemParticles;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public @NotNull Set<String> permissions() {
|
||||
return permissions;
|
||||
|
@ -78,6 +78,7 @@ public class PlayerStats {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Finally run a stat update after all modifiers have been gathered in the packet
|
||||
packet.runUpdate();
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ public abstract class EquippedItem {
|
||||
public void cacheItem() {
|
||||
Validate.isTrue(cached == null, "MMOItem has already been cached");
|
||||
cached = new VolatileMMOItem(item);
|
||||
MMOItems.log("Cached item " + cached.getType().getName() + " in slot " + slot);
|
||||
}
|
||||
|
||||
public NBTItem getNBT() {
|
||||
|
@ -1,98 +1,176 @@
|
||||
package net.Indyuce.mmoitems.comp.inventory;
|
||||
|
||||
import io.lumine.mythic.lib.api.player.EquipmentSlot;
|
||||
import io.lumine.mythic.lib.player.modifier.ModifierSource;
|
||||
import io.lumine.mythic.lib.player.skill.PassiveSkill;
|
||||
import net.Indyuce.mmoitems.ItemStats;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.player.inventory.EquippedItem;
|
||||
import net.Indyuce.mmoitems.comp.inventory.model.PlayerInventory;
|
||||
import org.bukkit.Bukkit;
|
||||
import net.Indyuce.mmoitems.api.item.mmoitem.VolatileMMOItem;
|
||||
import net.Indyuce.mmoitems.api.player.PlayerData;
|
||||
import net.Indyuce.mmoitems.comp.inventory.model.PlayerInventoryImage;
|
||||
import net.Indyuce.mmoitems.comp.inventory.model.PlayerMMOInventory;
|
||||
import net.Indyuce.mmoitems.stat.data.*;
|
||||
import net.milkbowl.vault.permission.Permission;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Previously, only one Player Inventory was allowed.
|
||||
* This makes it so plugins may register all the Player Inventories they want.
|
||||
* <p></p>
|
||||
* For context, a 'Player Inventory' tells MMOItems where to look for equipped items,
|
||||
* (items that will add their stats to the player).
|
||||
* mmoitems
|
||||
* 17/01/2023
|
||||
*
|
||||
* @author Roch Blondiaux (Kiwix).
|
||||
*/
|
||||
public class PlayerInventoryHandler {
|
||||
public class PlayerInventoryHandler implements Runnable {
|
||||
|
||||
/**
|
||||
* Gets the registered Player Inventories --- The places where MMOItems determines player equipped items.
|
||||
*/
|
||||
@NotNull
|
||||
private final List<PlayerInventory> registeredInventories = new ArrayList<>();
|
||||
private volatile boolean running = false;
|
||||
private final PlayerData data;
|
||||
private final Player player;
|
||||
private final PlayerMMOInventory inventory;
|
||||
private PlayerInventoryImage image;
|
||||
|
||||
/**
|
||||
* Use this to tell MMOItems to search equipment here ---
|
||||
* Items that will give their stats to the player.
|
||||
* <p></p>
|
||||
* Note that if the items are not held in the correct slot (OFF_CATALYST not held in OFFHAND)
|
||||
* they won't provide their stats. You may fool MMOItems by telling the <code>EquippedItem</code> that it is
|
||||
* in their correct slot though, but that is up to you.
|
||||
* <p></p>
|
||||
* <b>Calling twice will cause duplicates</b> but is nevertheless allowed if you really want to.
|
||||
*/
|
||||
public void register(@NotNull PlayerInventory pInventory) {
|
||||
registeredInventories.add(pInventory);
|
||||
if (pInventory instanceof Listener)
|
||||
Bukkit.getPluginManager().registerEvents((Listener) pInventory, MMOItems.plugin);
|
||||
public PlayerInventoryHandler(@NotNull PlayerData data, @NotNull PlayerMMOInventory inventory) {
|
||||
this.data = data;
|
||||
this.inventory = inventory;
|
||||
this.player = data.getPlayer();
|
||||
this.image = PlayerInventoryImage.make(data);
|
||||
}
|
||||
|
||||
public void unregisterIf(Predicate<PlayerInventory> filter) {
|
||||
Iterator<PlayerInventory> iterator = registeredInventories.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
PlayerInventory next = iterator.next();
|
||||
if (filter.test(next)) {
|
||||
if (next instanceof Listener)
|
||||
HandlerList.unregisterAll((Listener) next);
|
||||
iterator.remove();
|
||||
@Override
|
||||
public void run() {
|
||||
if (!data.getPlayer().isOnline()) this.running = false;
|
||||
if (!running) return;
|
||||
|
||||
// Create a new image and compare it to the old one
|
||||
final PlayerInventoryImage newImage = PlayerInventoryImage.make(data);
|
||||
if (!newImage.isDifferent(image))
|
||||
return;
|
||||
|
||||
newImage.difference(this.image)
|
||||
.forEach(item -> {
|
||||
final int slot = item.getSlotNumber();
|
||||
final VolatileMMOItem oldItem = image.getCached(slot).filter(volatileMMOItem -> !volatileMMOItem.getId().isEmpty()).orElse(null);
|
||||
final VolatileMMOItem newItem = (item.getItem() == null || item.getItem().getType().isAir()) ? null : new VolatileMMOItem(item.getNBT());
|
||||
|
||||
// TODO: remove the following line
|
||||
MMOItems.log(String.format("Slot #%d: %s -> %s", slot, oldItem == null ? "AIR" : oldItem.getId(), newItem == null ? "AIR" : newItem.getId()));
|
||||
|
||||
// Process old item
|
||||
if (oldItem != null) {
|
||||
// Potion effects
|
||||
if (oldItem.hasData(ItemStats.PERM_EFFECTS))
|
||||
((PotionEffectListData) oldItem.getData(ItemStats.PERM_EFFECTS)).getEffects()
|
||||
.stream()
|
||||
.filter(e -> this.data.getPermanentPotionEffectAmplifier(e.getType()) == e.getLevel() - 1)
|
||||
.forEach(e -> this.data.getPermanentPotionEffectsMap().remove(e.getType(), e.toEffect()));
|
||||
|
||||
// Abilities
|
||||
|
||||
// Item particles
|
||||
if (oldItem.hasData(ItemStats.ITEM_PARTICLES)) {
|
||||
ParticleData particleData = (ParticleData) oldItem.getData(ItemStats.ITEM_PARTICLES);
|
||||
if (particleData.getType().hasPriority())
|
||||
this.data.resetOverridingItemParticles();
|
||||
else
|
||||
this.data.getItemParticles().removeIf(particleRunnable -> particleRunnable.getParticleData().equals(particleData));
|
||||
}
|
||||
|
||||
// Permissions
|
||||
if (MMOItems.plugin.hasPermissions() && oldItem.hasData(ItemStats.GRANTED_PERMISSIONS)) {
|
||||
final Permission perms = MMOItems.plugin.getVault().getPermissions();
|
||||
((StringListData) oldItem.getData(ItemStats.GRANTED_PERMISSIONS)).getList()
|
||||
.forEach(s -> {
|
||||
this.data.permissions().remove(s);
|
||||
perms.playerRemove(player, s);
|
||||
});
|
||||
}
|
||||
|
||||
// Remove the item from the inventory
|
||||
this.inventory.remove(slot);
|
||||
}
|
||||
|
||||
// Process new item
|
||||
if (newItem != null) {
|
||||
if (!item.isPlacementLegal() || !this.data.getRPG().canUse(item.getNBT(), false, false))
|
||||
return;
|
||||
|
||||
// Cache item and add it to the inventory
|
||||
item.cacheItem();
|
||||
this.inventory.addItem(item);
|
||||
|
||||
// Abilities
|
||||
if (newItem.hasData(ItemStats.ABILITIES))
|
||||
for (AbilityData abilityData : ((AbilityListData) newItem.getData(ItemStats.ABILITIES)).getAbilities()) {
|
||||
ModifierSource modSource = item.getCached().getType().getModifierSource();
|
||||
this.data.getMMOPlayerData().getPassiveSkillMap().addModifier(new PassiveSkill("MMOItemsItem", abilityData, item.getSlot(), modSource));
|
||||
}
|
||||
|
||||
// Modifier application rules
|
||||
final ModifierSource source = newItem.getType().getModifierSource();
|
||||
final EquipmentSlot equipmentSlot = item.getSlot();
|
||||
if (!EquipmentSlot.MAIN_HAND.isCompatible(source, equipmentSlot))
|
||||
return;
|
||||
|
||||
// Potion effects
|
||||
if (newItem.hasData(ItemStats.PERM_EFFECTS))
|
||||
((PotionEffectListData) newItem.getData(ItemStats.PERM_EFFECTS)).getEffects()
|
||||
.stream()
|
||||
.filter(e -> this.data.getPermanentPotionEffectAmplifier(e.getType()) < e.getLevel() - 1)
|
||||
.forEach(effect -> this.data.getPermanentPotionEffectsMap().put(effect.getType(), effect.toEffect()));
|
||||
|
||||
// Item particles
|
||||
if (newItem.hasData(ItemStats.ITEM_PARTICLES)) {
|
||||
ParticleData particleData = (ParticleData) newItem.getData(ItemStats.ITEM_PARTICLES);
|
||||
if (particleData.getType().hasPriority()) {
|
||||
if (this.data.getOverridingItemParticles() == null)
|
||||
this.data.setOverridingItemParticles(particleData.start(this.data));
|
||||
} else
|
||||
this.data.getItemParticles().add(particleData.start(this.data));
|
||||
}
|
||||
|
||||
// Permissions
|
||||
if (MMOItems.plugin.hasPermissions() && newItem.hasData(ItemStats.GRANTED_PERMISSIONS)) {
|
||||
final Permission perms = MMOItems.plugin.getVault().getPermissions();
|
||||
this.data.permissions().addAll(((StringListData) newItem.getData(ItemStats.GRANTED_PERMISSIONS)).getList());
|
||||
this.data.permissions()
|
||||
.stream()
|
||||
.filter(s -> !perms.has(player, s))
|
||||
.forEach(perm -> perms.playerAdd(player, perm));
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// Calculate player stats
|
||||
this.data.getStats().updateStats();
|
||||
|
||||
// Update stats from external plugins
|
||||
MMOItems.plugin.getRPG().refreshStats(this.data);
|
||||
|
||||
// Cache the new image
|
||||
this.image = newImage;
|
||||
this.image.cache();
|
||||
|
||||
// Stop the task
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
/* Task */
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
running = true;
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
PlayerInventoryHandler.this.run();
|
||||
}
|
||||
}
|
||||
}.runTaskAsynchronously(MMOItems.plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used by external plugins to clear current inventory
|
||||
* handlers if you want offhand and mainhand items removed
|
||||
* from the player inventory
|
||||
*/
|
||||
public void unregisterAll() {
|
||||
|
||||
// Unregister events
|
||||
for (PlayerInventory inv : registeredInventories)
|
||||
if (inv instanceof Listener)
|
||||
HandlerList.unregisterAll((Listener) inv);
|
||||
|
||||
registeredInventories.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A copy of the list of registered inventories.
|
||||
*/
|
||||
public ArrayList<PlayerInventory> getAll() {
|
||||
return new ArrayList<>(registeredInventories);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Gets the totality of items from all the PlayerInventories ie all the items that will add their stats to the player.
|
||||
*/
|
||||
@NotNull
|
||||
public List<EquippedItem> getInventory(@NotNull Player player) {
|
||||
// Get and add lists from every registered inventories
|
||||
ArrayList<EquippedItem> cummulative = new ArrayList<>();
|
||||
// For every registered inventory
|
||||
for (PlayerInventory inv : registeredInventories) {
|
||||
// Get
|
||||
cummulative.addAll(inv.getInventory(player));
|
||||
}
|
||||
Bukkit.broadcastMessage("Cummulative: " + cummulative.size() + " PlayerInventoryHandler#93");
|
||||
// Return thay result
|
||||
return cummulative;
|
||||
public void stop() {
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
|
@ -1,308 +0,0 @@
|
||||
package net.Indyuce.mmoitems.comp.inventory;
|
||||
|
||||
import io.lumine.mythic.lib.api.player.EquipmentSlot;
|
||||
import io.lumine.mythic.lib.player.modifier.ModifierSource;
|
||||
import io.lumine.mythic.lib.player.skill.PassiveSkill;
|
||||
import net.Indyuce.mmoitems.ItemStats;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.ItemSet;
|
||||
import net.Indyuce.mmoitems.api.event.inventory.MMOItemEquipEvent;
|
||||
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
|
||||
import net.Indyuce.mmoitems.api.player.PlayerData;
|
||||
import net.Indyuce.mmoitems.api.player.inventory.EquippedItem;
|
||||
import net.Indyuce.mmoitems.comp.inventory.model.SlotEquippedItem;
|
||||
import net.Indyuce.mmoitems.stat.data.*;
|
||||
import net.milkbowl.vault.permission.Permission;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* mmoitems
|
||||
* 17/01/2023
|
||||
*
|
||||
* @author Roch Blondiaux (Kiwix).
|
||||
*/
|
||||
public class PlayerInventoryUpdater implements Runnable {
|
||||
|
||||
private final PlayerData data;
|
||||
private final Map<Integer, Integer> lastHashCodes = new HashMap<>();
|
||||
private final Map<Integer, SlotEquippedItem> CACHE = new HashMap<>();
|
||||
private final Map<Integer, List<UUID>> MODIFIERS_CACHE = new HashMap<>();
|
||||
|
||||
private boolean running;
|
||||
|
||||
public PlayerInventoryUpdater(PlayerData data) {
|
||||
this.data = data;
|
||||
this.running = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!this.running)
|
||||
return;
|
||||
if (!this.data.isOnline()) {
|
||||
this.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
// Refresh item by item
|
||||
MMOItems.plugin.getInventory()
|
||||
.inventory(data.getPlayer())
|
||||
.stream()
|
||||
.filter(item -> item instanceof SlotEquippedItem)
|
||||
.map(item -> (SlotEquippedItem) item)
|
||||
.filter(this::needsUpdate)
|
||||
.forEach(eItem -> {
|
||||
final int currentHashcode = lastHashCodes.getOrDefault(eItem.getSlotNumber(), -1);
|
||||
final int newHashcode = isEmpty(eItem) ? -1 : eItem.hashCode();
|
||||
final SlotEquippedItem oldItem = CACHE.get(currentHashcode);
|
||||
|
||||
if (currentHashcode == newHashcode)
|
||||
return;
|
||||
|
||||
// Call item equip event
|
||||
Bukkit.getPluginManager().callEvent(new MMOItemEquipEvent(currentHashcode, newHashcode, oldItem, eItem));
|
||||
MMOItems.log("Calling item equip event for slot: " + eItem.getSlotNumber() + " with hashcodes: " + currentHashcode + " -> " + newHashcode);
|
||||
|
||||
this.removeStats(oldItem, currentHashcode);
|
||||
|
||||
this.addStats(eItem, newHashcode);
|
||||
});
|
||||
|
||||
// Refresh item sets
|
||||
List<EquippedItem> equippedItems = MMOItems.plugin.getInventory()
|
||||
.inventory(data.getPlayer());
|
||||
this.recalculateItemSet(equippedItems);
|
||||
|
||||
// Calculate player stats
|
||||
this.data.getStats().updateStats();
|
||||
|
||||
// Update stats from external plugins
|
||||
MMOItems.plugin.getRPG().refreshStats(this.data);
|
||||
|
||||
// TODO: Call inventory refresh event
|
||||
// Bukkit.getPluginManager().callEvent(new MMOInventoryRefreshEvent(inventory.equipped(), getPlayer(), this));
|
||||
}
|
||||
|
||||
private void recalculateItemSet(@NotNull List<EquippedItem> equippedItems) {
|
||||
final Map<ItemSet, Integer> itemSetCount = new HashMap<>();
|
||||
for (EquippedItem equipped : equippedItems) {
|
||||
final String tag = equipped.getNBT().getString("MMOITEMS_ITEM_SET");
|
||||
final @Nullable ItemSet itemSet = MMOItems.plugin.getSets().get(tag);
|
||||
if (itemSet == null)
|
||||
continue;
|
||||
itemSetCount.put(itemSet, itemSetCount.getOrDefault(itemSet, 0) + 1);
|
||||
}
|
||||
|
||||
// Reset and compute item set bonuses
|
||||
this.data.resetSetBonuses();
|
||||
for (Map.Entry<ItemSet, Integer> equippedSetBonus : itemSetCount.entrySet()) {
|
||||
if (this.data.getSetBonuses() == null)
|
||||
this.data.setSetBonuses(equippedSetBonus.getKey().getBonuses(equippedSetBonus.getValue()));
|
||||
else
|
||||
this.data.getSetBonuses().merge(equippedSetBonus.getKey().getBonuses(equippedSetBonus.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all stats from the old item
|
||||
*
|
||||
* @param oldItem The old item
|
||||
*/
|
||||
private void removeStats(@Nullable SlotEquippedItem oldItem, int hashcode) {
|
||||
// Remove all old item attributes & effects
|
||||
if (oldItem == null || isEmpty(oldItem))
|
||||
return;
|
||||
MMOItems.log("Removing old item attributes & effects");
|
||||
final MMOItem mmoItem = oldItem.getCached().clone();
|
||||
this.data.getInventory().remove(oldItem);
|
||||
|
||||
// Potion effects
|
||||
if (mmoItem.hasData(ItemStats.PERM_EFFECTS)) {
|
||||
((PotionEffectListData) mmoItem.getData(ItemStats.PERM_EFFECTS))
|
||||
.getEffects()
|
||||
.forEach(effect -> {
|
||||
this.data.getPermanentPotionEffectsMap().remove(effect.getType(), effect.toEffect());
|
||||
MMOItems.log("Pot effect removed: " + effect.getType());
|
||||
});
|
||||
}
|
||||
|
||||
// Item particles
|
||||
if (mmoItem.hasData(ItemStats.ITEM_PARTICLES)) {
|
||||
ParticleData particleData = (ParticleData) mmoItem.getData(ItemStats.ITEM_PARTICLES);
|
||||
|
||||
if (particleData.getType().hasPriority()) {
|
||||
if (this.data.getOverridingItemParticles() != null
|
||||
&& this.data.getOverridingItemParticles().getParticleData().equals(particleData)) {
|
||||
this.data.getOverridingItemParticles().cancel();
|
||||
this.data.resetOverridingItemParticles();
|
||||
MMOItems.log("Overriding particle removed: " + particleData.getType());
|
||||
}
|
||||
} else {
|
||||
this.data.getItemParticles()
|
||||
.stream()
|
||||
.filter(particleRunnable -> particleRunnable.getParticleData().equals(particleData))
|
||||
.peek(particleRunnable -> MMOItems.log("Particle removed: " + particleRunnable.getParticleData().getType()))
|
||||
.forEach(BukkitRunnable::cancel);
|
||||
this.data.getItemParticles().removeIf(BukkitRunnable::isCancelled);
|
||||
}
|
||||
}
|
||||
|
||||
// Permissions
|
||||
if (MMOItems.plugin.hasPermissions() && mmoItem.hasData(ItemStats.GRANTED_PERMISSIONS)) {
|
||||
final Permission perms = MMOItems.plugin.getVault().getPermissions();
|
||||
List<String> permissions = new ArrayList<>(((StringListData) mmoItem.getData(ItemStats.GRANTED_PERMISSIONS)).getList());
|
||||
permissions.forEach(s -> {
|
||||
this.data.permissions().remove(s);
|
||||
MMOItems.log("Perm removed: " + s);
|
||||
if (perms.has(this.data.getPlayer(), s))
|
||||
perms.playerRemove(this.data.getPlayer(), s);
|
||||
});
|
||||
}
|
||||
|
||||
// Abilities
|
||||
// TODO: find a solution for that:
|
||||
// Idea 1: cache ability uuid and remove it from the map
|
||||
if (mmoItem.hasData(ItemStats.ABILITIES))
|
||||
MODIFIERS_CACHE.getOrDefault(hashcode, Collections.emptyList())
|
||||
.forEach(uuid -> this.data.getMMOPlayerData()
|
||||
.getPassiveSkillMap()
|
||||
.getModifiers()
|
||||
.removeIf(passiveSkill -> passiveSkill.getUniqueId().equals(uuid)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all new item attributes & effects
|
||||
*
|
||||
* @param eItem The new item
|
||||
*/
|
||||
private void addStats(@NotNull SlotEquippedItem eItem, int hashcode) {
|
||||
MMOItems.log("Adding new item attributes & effects");
|
||||
|
||||
// Cache new item hashcode & item
|
||||
cache(eItem);
|
||||
|
||||
// Check if the new item is empty
|
||||
if (isEmpty(eItem)) {
|
||||
MMOItems.log("New item is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if item is legal
|
||||
if (!eItem.isPlacementLegal() || !this.data.getRPG().canUse(eItem.getNBT(), false, false)) {
|
||||
if (!eItem.isPlacementLegal())
|
||||
MMOItems.log("Illegal item placement detected.");
|
||||
else
|
||||
MMOItems.log("Illegal item usage detected.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache item
|
||||
eItem.cacheItem();
|
||||
|
||||
// Add item to MMO inventory
|
||||
this.data.getInventory().addItem(eItem);
|
||||
|
||||
// Add all new item attributes & effects
|
||||
final MMOItem mmoItem = eItem.getCached();
|
||||
final EquipmentSlot equipmentSlot = eItem.getSlot();
|
||||
|
||||
// Abilities
|
||||
MMOItems.log("Adding abilities");
|
||||
if (mmoItem.hasData(ItemStats.ABILITIES)) {
|
||||
for (AbilityData abilityData : ((AbilityListData) mmoItem.getData(ItemStats.ABILITIES)).getAbilities()) {
|
||||
|
||||
MMOItems.log("Ability found: " + abilityData.getAbility().getName());
|
||||
ModifierSource modSource = eItem.getCached().getType().getModifierSource();
|
||||
PassiveSkill skill = this.data.getMMOPlayerData()
|
||||
.getPassiveSkillMap()
|
||||
.addModifier(new PassiveSkill("MMOItemsItem", abilityData, equipmentSlot, modSource));
|
||||
MODIFIERS_CACHE.computeIfAbsent(hashcode, i -> new ArrayList<>()).add(skill.getUniqueId());
|
||||
MMOItems.log("Ability added: " + abilityData.getTrigger());
|
||||
}
|
||||
} else
|
||||
MMOItems.log("No abilities found");
|
||||
|
||||
// Modifier application rules
|
||||
final ModifierSource source = mmoItem.getType().getModifierSource();
|
||||
MMOItems.log("Modifier source: " + source.name());
|
||||
if (!EquipmentSlot.MAIN_HAND.isCompatible(source, equipmentSlot)) {
|
||||
MMOItems.log("Modifier source is not compatible with equipment slot");
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply permanent potion effects
|
||||
MMOItems.log("Adding permanent potion effects");
|
||||
if (mmoItem.hasData(ItemStats.PERM_EFFECTS)) {
|
||||
MMOItems.log("Permanent potion effects found");
|
||||
((PotionEffectListData) mmoItem.getData(ItemStats.PERM_EFFECTS))
|
||||
.getEffects()
|
||||
.stream()
|
||||
.filter(potionEffectData -> this.data.getPermanentPotionEffectAmplifier(potionEffectData.getType()) < potionEffectData.getLevel() - 1)
|
||||
.peek(potionEffectData -> MMOItems.log("Pot effect added: " + potionEffectData.getType()))
|
||||
.forEach(effect -> this.data.getPermanentPotionEffectsMap().put(effect.getType(), effect.toEffect()));
|
||||
} else
|
||||
MMOItems.log("No permanent potion effects found");
|
||||
|
||||
// Permissions
|
||||
MMOItems.log("Adding permissions");
|
||||
if (MMOItems.plugin.hasPermissions() && mmoItem.hasData(ItemStats.GRANTED_PERMISSIONS)) {
|
||||
MMOItems.log("Permissions found");
|
||||
final Permission perms = MMOItems.plugin.getVault().getPermissions();
|
||||
this.data.permissions().addAll(((StringListData) mmoItem.getData(ItemStats.GRANTED_PERMISSIONS)).getList());
|
||||
this.data.permissions()
|
||||
.stream()
|
||||
.filter(s -> !perms.has(this.data.getPlayer(), s))
|
||||
.peek(s -> MMOItems.log("Perm added: " + s))
|
||||
.forEach(perm -> perms.playerAdd(this.data.getPlayer(), perm));
|
||||
} else
|
||||
MMOItems.log("No permissions found");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the item needs to be updated
|
||||
*
|
||||
* @param item The item to check
|
||||
* @return True if the item needs to be updated
|
||||
*/
|
||||
private boolean needsUpdate(@NotNull SlotEquippedItem item) {
|
||||
return !lastHashCodes.containsKey(item.getSlotNumber()) || lastHashCodes.get(item.getSlotNumber()) != (isEmpty(item) ? -1 : item.hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache the item
|
||||
*
|
||||
* @param item The item to cache
|
||||
*/
|
||||
private void cache(@NotNull SlotEquippedItem item) {
|
||||
final int hashCode = isEmpty(item) ? -1 : item.hashCode();
|
||||
lastHashCodes.put(item.getSlotNumber(), hashCode);
|
||||
CACHE.put(hashCode, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the item is empty
|
||||
*
|
||||
* @param item The item to check
|
||||
* @return True if the item is empty
|
||||
*/
|
||||
private boolean isEmpty(@Nullable SlotEquippedItem item) {
|
||||
return item == null || item.getNBT() == null || item.getNBT().getItem() == null || item.getNBT().getItem().getType().isAir();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
this.running = true;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
package net.Indyuce.mmoitems.comp.inventory.model;
|
||||
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.item.mmoitem.VolatileMMOItem;
|
||||
import net.Indyuce.mmoitems.api.player.PlayerData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* mmoitems
|
||||
* 24/01/2023
|
||||
*
|
||||
* @author Roch Blondiaux (Kiwix).
|
||||
*/
|
||||
public class PlayerInventoryImage {
|
||||
|
||||
private final PlayerData data;
|
||||
private final List<SlotEquippedItem> equipped;
|
||||
private final Map<Integer, VolatileMMOItem> cache;
|
||||
private final Map<Integer, Integer> hashCodes;
|
||||
private final long timestamp;
|
||||
|
||||
public PlayerInventoryImage(@NotNull PlayerData data) {
|
||||
this.data = data;
|
||||
this.equipped = new ArrayList<>();
|
||||
this.hashCodes = new HashMap<>();
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
this.cache = new HashMap<>();
|
||||
}
|
||||
|
||||
public @NotNull PlayerData data() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public @NotNull List<SlotEquippedItem> equipped() {
|
||||
return equipped;
|
||||
}
|
||||
|
||||
public @NotNull Map<Integer, Integer> hashCodes() {
|
||||
return hashCodes;
|
||||
}
|
||||
|
||||
public long timestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public @NotNull Optional<SlotEquippedItem> get(int slot) {
|
||||
return equipped.stream().filter(e -> e.getSlotNumber() == slot).findFirst();
|
||||
}
|
||||
|
||||
public @NotNull Optional<VolatileMMOItem> getCached(int slot) {
|
||||
return Optional.ofNullable(cache.get(slot));
|
||||
}
|
||||
|
||||
public void cache() {
|
||||
this.cache.clear();
|
||||
this.equipped.stream()
|
||||
.filter(item -> item.getNBT() != null)
|
||||
.forEach(e -> this.cache.put(e.getSlotNumber(), new VolatileMMOItem(e.getNBT())));
|
||||
}
|
||||
|
||||
public boolean isDifferent(@NotNull PlayerInventoryImage image) {
|
||||
return image.equipped.size() != equipped.size() || !hashCodes.equals(image.hashCodes());
|
||||
}
|
||||
|
||||
public List<SlotEquippedItem> difference(@NotNull PlayerInventoryImage image) {
|
||||
if (!isDifferent(image)) return Collections.emptyList();
|
||||
return hashCodes.entrySet()
|
||||
.stream()
|
||||
.filter(e -> !Objects.equals(e.getValue(), image.hashCodes().get(e.getKey())))
|
||||
.map(Map.Entry::getKey)
|
||||
.map(i -> equipped.stream().filter(e -> e.getSlotNumber() == i).findFirst().orElse(null))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static @NotNull PlayerInventoryImage make(@NotNull PlayerData data) {
|
||||
PlayerInventoryImage image = new PlayerInventoryImage(data);
|
||||
MMOItems.plugin.getInventory()
|
||||
.inventory(data.getPlayer())
|
||||
.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(i -> i instanceof SlotEquippedItem)
|
||||
.map(i -> (SlotEquippedItem) i)
|
||||
.forEach(i -> {
|
||||
image.equipped.add(i);
|
||||
image.hashCodes.put(i.getSlotNumber(), isEmpty(i) ? -1 : i.hashCode());
|
||||
});
|
||||
return image;
|
||||
}
|
||||
|
||||
private static boolean isEmpty(@Nullable SlotEquippedItem item) {
|
||||
return item == null || item.getItem() == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return equipped.hashCode();
|
||||
}
|
||||
}
|
@ -1,13 +1,15 @@
|
||||
package net.Indyuce.mmoitems.comp.inventory.model;
|
||||
|
||||
import io.lumine.mythic.lib.api.player.EquipmentSlot;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.player.PlayerData;
|
||||
import net.Indyuce.mmoitems.api.player.inventory.EquippedItem;
|
||||
import net.Indyuce.mmoitems.comp.inventory.PlayerInventoryUpdater;
|
||||
import net.Indyuce.mmoitems.comp.inventory.PlayerInventoryHandler;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* mmoitems
|
||||
@ -18,25 +20,33 @@ import java.util.*;
|
||||
public class PlayerMMOInventory {
|
||||
|
||||
private final UUID uniqueId;
|
||||
private final PlayerData data;
|
||||
private final Map<EquippedItem, Integer> content = new HashMap<>();
|
||||
private final List<EquippedItem> content = new ArrayList<>();
|
||||
private final PlayerInventoryHandler handler;
|
||||
|
||||
public PlayerMMOInventory(@NotNull PlayerData data) {
|
||||
this.uniqueId = data.getUniqueId();
|
||||
this.data = data;
|
||||
this.updater = new PlayerInventoryUpdater(data);
|
||||
this.handler = new PlayerInventoryHandler(data, this);
|
||||
}
|
||||
|
||||
public void addItem(@NotNull EquippedItem item) {
|
||||
content.put(item, item.hashCode());
|
||||
content.add(item);
|
||||
}
|
||||
|
||||
public void remove(@NotNull EquippedItem item) {
|
||||
content.remove(item);
|
||||
}
|
||||
|
||||
public void remove(@NotNull EquipmentSlot slot) {
|
||||
content.keySet().removeIf((item) -> item.getSlot() == slot);
|
||||
public void remove(int slot) {
|
||||
content.removeIf(item -> item instanceof SlotEquippedItem && ((SlotEquippedItem) item).getSlotNumber() == slot);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
handler.start();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@ApiStatus.ScheduledForRemoval
|
||||
public void scheduleUpdate() {
|
||||
}
|
||||
|
||||
/* Getters */
|
||||
@ -45,42 +55,6 @@ public class PlayerMMOInventory {
|
||||
}
|
||||
|
||||
public List<EquippedItem> equipped() {
|
||||
return Collections.unmodifiableList(new ArrayList<>(content.keySet()));
|
||||
}
|
||||
|
||||
public List<Integer> hashCodes() {
|
||||
return Collections.unmodifiableList(new ArrayList<>(content.values()));
|
||||
}
|
||||
|
||||
public int hashCode(EquipmentSlot slot) {
|
||||
return content.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> entry.getKey().getSlot() == slot)
|
||||
.map(Map.Entry::getValue)
|
||||
.findFirst()
|
||||
.orElse(0);
|
||||
}
|
||||
|
||||
|
||||
public Map<EquippedItem, Integer> content() {
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(uniqueId, content.keySet());
|
||||
}
|
||||
|
||||
// Old weird code
|
||||
@Deprecated
|
||||
public void scheduleUpdate() {
|
||||
MMOItems.log("PlayerMMOInventory#scheduleUpdate called!");
|
||||
}
|
||||
|
||||
private final PlayerInventoryUpdater updater;
|
||||
@Deprecated
|
||||
public void updateCheck(){
|
||||
this.updater.run();
|
||||
// MMOItems.log("PlayerMMOInventory#updateCheck called!");
|
||||
return Collections.unmodifiableList(content);
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SlotEquippedItem extends EquippedItem {
|
||||
public class SlotEquippedItem extends EquippedItem implements Cloneable {
|
||||
private final Player player;
|
||||
private final int slotNumber;
|
||||
|
||||
@ -90,4 +90,13 @@ public class SlotEquippedItem extends EquippedItem {
|
||||
accumulator ^= getItem().hashCode();
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SlotEquippedItem clone() {
|
||||
try {
|
||||
return (SlotEquippedItem) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,63 @@
|
||||
package net.Indyuce.mmoitems.listener;
|
||||
|
||||
import io.lumine.mythic.lib.api.event.armorequip.ArmorEquipEvent;
|
||||
import net.Indyuce.mmoitems.api.player.PlayerData;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.inventory.InventoryDragEvent;
|
||||
import org.bukkit.event.inventory.InventoryMoveItemEvent;
|
||||
import org.bukkit.event.player.PlayerDropItemEvent;
|
||||
import org.bukkit.event.player.PlayerItemHeldEvent;
|
||||
import org.bukkit.event.player.PlayerSwapHandItemsEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* mmoitems
|
||||
* 24/01/2023
|
||||
*
|
||||
* @author Roch Blondiaux (Kiwix).
|
||||
*/
|
||||
public class InventoryListener implements Listener {
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onItemDrop(PlayerDropItemEvent e) {
|
||||
if (!e.isCancelled())
|
||||
triggerUpdate(e.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onHandSwap(PlayerSwapHandItemsEvent e) {
|
||||
if (!e.isCancelled())
|
||||
triggerUpdate(e.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onSlotChange(PlayerItemHeldEvent e) {
|
||||
if (!e.isCancelled())
|
||||
triggerUpdate(e.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onItemDrag(InventoryDragEvent e) {
|
||||
if (!e.isCancelled())
|
||||
triggerUpdate((Player) e.getView().getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onItemMove(InventoryMoveItemEvent e) {
|
||||
if (!e.isCancelled() && e.getDestination().getHolder() instanceof Player)
|
||||
triggerUpdate((Player) e.getDestination().getHolder());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onArmorEquip(ArmorEquipEvent e) {
|
||||
if (!e.isCancelled())
|
||||
triggerUpdate(e.getPlayer());
|
||||
}
|
||||
|
||||
private void triggerUpdate(@NotNull Player player) {
|
||||
PlayerData.get(player).getInventory().update();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user