Fixed NPE with guild gui

This commit is contained in:
Jules 2024-02-19 12:52:44 +01:00
parent d5ffd04117
commit 1a1346d3bc
12 changed files with 312 additions and 247 deletions

View File

@ -6,10 +6,7 @@ import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.hologram.Hologram;
import io.lumine.mythic.lib.version.VersionMaterial;
import net.Indyuce.mmocore.MMOCore;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.*;
import org.bukkit.attribute.Attribute;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
@ -34,6 +31,20 @@ public class MMOCoreUtils {
return item != null && item.hasItemMeta() && item.getItemMeta().hasDisplayName();
}
/**
* If a given player is not in the server cache, no information
* cannot be retrieved from that player (without using requests
* to MC servers obviously). In that case, the instance of
* OfflinePlayer is pretty much useless and it only wraps its
* UUID which was already known beforehand.
*
* @param player Offline player instance to test
* @return Is the instance valid
*/
public static boolean isInvalid(OfflinePlayer player) {
return player.getName() == null;
}
public static String displayName(ItemStack item) {
return item.hasItemMeta() && item.getItemMeta().hasDisplayName() ? item.getItemMeta().getDisplayName()
: caseOnWords(item.getType().name().replace("_", " "));

View File

@ -22,10 +22,10 @@ import net.Indyuce.mmocore.party.AbstractParty;
import net.Indyuce.mmocore.player.stats.StatInfo;
import org.apache.commons.lang.Validate;
import org.bukkit.ChatColor;
import org.bukkit.OfflinePlayer;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
@ -123,36 +123,26 @@ public class PlayerStats extends EditableInventory {
return new Placeholders() {
final net.Indyuce.mmocore.api.player.stats.PlayerStats stats = inv.target.getStats();
@Nullable
@Override
public String apply(OfflinePlayer player, String str) {
String explored = str;
// Internal placeholders
while (explored.contains("{") && explored.substring(explored.indexOf("{")).contains("}")) {
final int begin = explored.indexOf("{"), end = explored.indexOf("}");
final String holder = explored.substring(begin + 1, end);
String replaced;
public String getPlaceholder(String holder) {
if (holder.endsWith("_base")) {
final String stat = UtilityMethods.enumName(holder.substring(0, holder.length() - 5));
replaced = StatManager.format(stat, stats.getBase(stat));
} else if (holder.endsWith("_extra")) {
return StatManager.format(stat, stats.getBase(stat));
}
if (holder.endsWith("_extra")) {
final String stat = UtilityMethods.enumName(holder.substring(0, holder.length() - 6));
replaced = StatManager.format(stat, MythicLib.plugin.getStats().getTotalValue(stat, stats.getMap()) - stats.getBase(stat));
} else if (holder.startsWith("attribute_")) {
return StatManager.format(stat, MythicLib.plugin.getStats().getTotalValue(stat, stats.getMap()) - stats.getBase(stat));
}
if (holder.startsWith("attribute_")) {
final PlayerAttribute attr = MMOCore.plugin.attributeManager.get(holder.substring(10).replace("_", "-").toLowerCase());
replaced = String.valueOf(inv.target.getAttributes().getAttribute(attr));
} else {
return String.valueOf(inv.target.getAttributes().getAttribute(attr));
}
final String stat = UtilityMethods.enumName(holder);
replaced = StatManager.format(stat, MythicLib.plugin.getStats().getTotalValue(stat, stats.getMap()));
}
str = str.replace("{" + holder + "}", replaced);
// Increase counter
explored = explored.substring(end + 1);
}
// External placeholders
return MMOCore.plugin.placeholderParser.parse(player, str);
return StatManager.format(stat, MythicLib.plugin.getStats().getTotalValue(stat, stats.getMap()));
}
};
}
@ -219,7 +209,7 @@ public class PlayerStats extends EditableInventory {
Placeholders holders = new Placeholders();
int count = inv.target.getParty().getOnlineMembers().size();
holders.register("count", "" + count);
holders.register("count", String.valueOf(count));
for (StatModifier buff : MMOCore.plugin.partyManager.getBonuses())
holders.register("buff_" + buff.getStat().toLowerCase(), buff.multiply(count - 1).toString());
@ -243,7 +233,7 @@ public class PlayerStats extends EditableInventory {
ItemStack disp = super.display(inv, n);
if (disp.getType() == VersionMaterial.PLAYER_HEAD.toMaterial()) {
SkullMeta meta = (SkullMeta) disp.getItemMeta();
inv.dynamicallyUpdateItem(this, n, disp, current -> {
inv.asyncUpdate(this, n, disp, current -> {
meta.setOwningPlayer(inv.target.getPlayer());
current.setItemMeta(meta);
});

View File

@ -13,6 +13,8 @@ import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public abstract class GeneratedInventory extends PluginInventory {
@ -32,7 +34,6 @@ public abstract class GeneratedInventory extends PluginInventory {
}
public EditableInventory getEditable() {
return editable;
}
@ -78,13 +79,12 @@ public abstract class GeneratedInventory extends PluginInventory {
adaptor.open();
}
/**
* @deprecated Not a fan of that implementation.
* Better work with {@link InventoryItem#setDisplayed(Inventory, GeneratedInventory)}
*/
@Deprecated
public void dynamicallyUpdateItem(InventoryItem<?> item, int n, ItemStack placed, Consumer<ItemStack> update) {
adaptor.dynamicallyUpdateItem(item, n, placed, update);
public void asyncUpdate(InventoryItem<?> item, int n, ItemStack placed, Consumer<ItemStack> update) {
adaptor.asyncUpdate(item, n, placed, update);
}
public <T> void asyncUpdate(CompletableFuture<T> future, InventoryItem<?> item, int n, ItemStack placed, BiConsumer<T, ItemStack> update) {
adaptor.asyncUpdate(future, item, n, placed, update);
}
@Override

View File

@ -4,6 +4,8 @@ import net.Indyuce.mmocore.gui.api.GeneratedInventory;
import net.Indyuce.mmocore.gui.api.item.InventoryItem;
import org.bukkit.inventory.ItemStack;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public abstract class Adaptor {
@ -17,6 +19,26 @@ public abstract class Adaptor {
public abstract void close();
@Deprecated
public abstract void dynamicallyUpdateItem(InventoryItem<?> item, int n, ItemStack placed, Consumer<ItemStack> update);
/**
* Applies a modification to an item in a new asynchronous thread.
* This is mostly used to apply owners to skulls as this creates a
* request to the Minecraft servers to get the player head.
*
* @param item Inventory item
* @param n Inventory item index
* @param placed Item already generated
* @param update What to do with the item
*/
public abstract void asyncUpdate(InventoryItem<?> item, int n, ItemStack placed, Consumer<ItemStack> update);
/**
* Applies a modification after the given future is complete.
*
* @param future Future being completed
* @param item Inventory item
* @param n Inventory item index
* @param placed Item already generated
* @param update What to do with the item
*/
public abstract <T> void asyncUpdate(CompletableFuture<T> future, InventoryItem<?> item, int n, ItemStack placed, BiConsumer<T, ItemStack> update);
}

View File

@ -9,6 +9,8 @@ import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class ClassicAdaptor extends Adaptor {
@ -29,13 +31,21 @@ public class ClassicAdaptor extends Adaptor {
}
@Override
public void dynamicallyUpdateItem(InventoryItem<?> item, int n, ItemStack placed, Consumer<ItemStack> update) {
public void asyncUpdate(InventoryItem<?> item, int n, ItemStack placed, Consumer<ItemStack> update) {
Bukkit.getScheduler().runTaskAsynchronously(MMOCore.plugin, () -> {
update.accept(placed);
open.setItem(item.getSlots().get(n), placed);
});
}
@Override
public <T> void asyncUpdate(CompletableFuture<T> future, InventoryItem<?> item, int n, ItemStack placed, BiConsumer<T, ItemStack> update) {
future.thenAccept(t -> {
update.accept(t, placed);
open.setItem(item.getSlots().get(n), placed);
});
}
public Inventory getInventory() {
Inventory inv = Bukkit.createInventory(generated, generated.getEditable().getSlots(), MythicLib.plugin.getPlaceholderParser().parse(generated.getPlayer(), generated.calculateName()));

View File

@ -10,7 +10,6 @@ import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.gui.api.GeneratedInventory;
import net.Indyuce.mmocore.gui.api.InventoryClickContext;
import net.Indyuce.mmocore.gui.api.item.InventoryItem;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.ArmorStand;
@ -34,6 +33,8 @@ import org.bukkit.util.Vector;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class ThreeDimAdaptor extends Adaptor {
@ -346,8 +347,13 @@ public class ThreeDimAdaptor extends Adaptor {
}
@Override
public void dynamicallyUpdateItem(InventoryItem<?> item, int n, ItemStack placed, Consumer<ItemStack> update) {
public void asyncUpdate(InventoryItem<?> item, int n, ItemStack placed, Consumer<ItemStack> update) {
// Not implemented
}
@Override
public <T> void asyncUpdate(CompletableFuture<T> future, InventoryItem<?> item, int n, ItemStack placed, BiConsumer<T, ItemStack> update) {
// Not implemented
}
private class SpawnPacketListener extends PacketAdapter {

View File

@ -0,0 +1,12 @@
package net.Indyuce.mmocore.gui.api.item;
import org.jetbrains.annotations.Nullable;
public class ErrorPlaceholders extends Placeholders {
@Nullable
@Override
public String getPlaceholder(String placeholder) {
return "???";
}
}

View File

@ -2,6 +2,7 @@ package net.Indyuce.mmocore.gui.api.item;
import net.Indyuce.mmocore.MMOCore;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
@ -10,11 +11,17 @@ import java.util.Map;
public class Placeholders {
private final Map<String, String> placeholders = new HashMap<>();
public void register(String path, Object obj) {
placeholders.put(path, obj.toString());
public void register(@Nullable String path, @Nullable Object obj) {
placeholders.put(path, String.valueOf(obj));
}
public String apply(OfflinePlayer player, String str) {
@Nullable
public String getPlaceholder(String placeholder) {
return placeholders.get(placeholder);
}
@NotNull
public String apply(@NotNull OfflinePlayer player, @NotNull String str) {
// Remove conditions first
str = removeCondition(str);
@ -30,7 +37,7 @@ public class Placeholders {
while (explored.contains("{") && explored.substring(explored.indexOf("{")).contains("}")) {
final int begin = explored.indexOf("{"), end = explored.indexOf("}");
final String holder = explored.substring(begin + 1, end);
@Nullable String found = placeholders.get(holder);
@Nullable String found = getPlaceholder(holder);
/*
* Do NOT replace the placeholder unless a corresponding value has
@ -38,8 +45,7 @@ public class Placeholders {
* math expansions which interferes with MMOCore placeholders since
* it uses {....} as well.
*/
if (found != null)
str = str.replace("{" + holder + "}", found);
if (found != null) str = str.replace("{" + holder + "}", found);
// Increase counter
explored = explored.substring(end + 1);

View File

@ -4,12 +4,14 @@ import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.ConfigMessage;
import net.Indyuce.mmocore.api.player.PlayerActivity;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.util.MMOCoreUtils;
import net.Indyuce.mmocore.api.util.input.ChatInput;
import net.Indyuce.mmocore.api.util.input.PlayerInput.InputType;
import net.Indyuce.mmocore.api.util.math.format.DelayFormat;
import net.Indyuce.mmocore.gui.api.EditableInventory;
import net.Indyuce.mmocore.gui.api.GeneratedInventory;
import net.Indyuce.mmocore.gui.api.InventoryClickContext;
import net.Indyuce.mmocore.gui.api.item.ErrorPlaceholders;
import net.Indyuce.mmocore.gui.api.item.InventoryItem;
import net.Indyuce.mmocore.gui.api.item.Placeholders;
import net.Indyuce.mmocore.gui.api.item.SimplePlaceholderItem;
@ -68,7 +70,7 @@ public class EditableFriendList extends EditableInventory {
return new FriendListInventory(data, this);
}
public static class OfflineFriendItem extends InventoryItem {
class OfflineFriendItem extends InventoryItem {
public OfflineFriendItem(FriendItem parent, ConfigurationSection config) {
super(parent, config);
}
@ -87,6 +89,7 @@ public class EditableFriendList extends EditableInventory {
@Override
public Placeholders getPlaceholders(GeneratedInventory inv, int n) {
OfflinePlayer friend = getEffectivePlayer(inv, n);
if (MMOCoreUtils.isInvalid(friend)) return new ErrorPlaceholders();
Placeholders holders = new Placeholders();
holders.register("name", friend.getName());
@ -95,7 +98,7 @@ public class EditableFriendList extends EditableInventory {
}
}
public static class OnlineFriendItem extends SimplePlaceholderItem {
class OnlineFriendItem extends SimplePlaceholderItem {
public OnlineFriendItem(FriendItem parent, ConfigurationSection config) {
super(parent, config);
}
@ -126,7 +129,7 @@ public class EditableFriendList extends EditableInventory {
}
}
public static class FriendItem extends SimplePlaceholderItem {
class FriendItem extends SimplePlaceholderItem {
private final OnlineFriendItem online;
private final OfflineFriendItem offline;
@ -150,7 +153,7 @@ public class EditableFriendList extends EditableInventory {
ItemMeta meta = disp.getItemMeta();
meta.getPersistentDataContainer().set(UUID_NAMESPACEDKEY, PersistentDataType.STRING, friend.getUniqueId().toString());
if (meta instanceof SkullMeta)
inv.dynamicallyUpdateItem(this, n, disp, current -> {
inv.asyncUpdate(this, n, disp, current -> {
((SkullMeta) meta).setOwningPlayer(friend);
current.setItemMeta(meta);
});
@ -170,7 +173,7 @@ public class EditableFriendList extends EditableInventory {
}
}
public class FriendListInventory extends GeneratedInventory {
class FriendListInventory extends GeneratedInventory {
private int page;
public FriendListInventory(PlayerData playerData, EditableInventory editable) {

View File

@ -78,7 +78,7 @@ public class EditableGuildAdmin extends EditableInventory {
meta.getPersistentDataContainer().set(UUID_NAMESPACEDKEY, PersistentDataType.STRING, uuid.toString());
if (meta instanceof SkullMeta && offlinePlayer != null)
inv.dynamicallyUpdateItem(this, n, disp, current -> {
inv.asyncUpdate(this, n, disp, current -> {
((SkullMeta) meta).setOwningPlayer(offlinePlayer);
current.setItemMeta(meta);
});

View File

@ -3,12 +3,14 @@ package net.Indyuce.mmocore.gui.social.guild;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.ConfigMessage;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.util.MMOCoreUtils;
import net.Indyuce.mmocore.api.util.input.ChatInput;
import net.Indyuce.mmocore.api.util.input.PlayerInput;
import net.Indyuce.mmocore.api.util.math.format.DelayFormat;
import net.Indyuce.mmocore.gui.api.EditableInventory;
import net.Indyuce.mmocore.gui.api.GeneratedInventory;
import net.Indyuce.mmocore.gui.api.InventoryClickContext;
import net.Indyuce.mmocore.gui.api.item.ErrorPlaceholders;
import net.Indyuce.mmocore.gui.api.item.InventoryItem;
import net.Indyuce.mmocore.gui.api.item.Placeholders;
import net.Indyuce.mmocore.gui.api.item.SimplePlaceholderItem;
@ -22,6 +24,7 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.SkullMeta;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.UUID;
@ -48,21 +51,23 @@ public class EditableGuildView extends EditableInventory {
return true;
}
@NotNull
@Override
public OfflinePlayer getEffectivePlayer(GuildViewInventory inv, int n) {
return Bukkit.getOfflinePlayer(inv.members.get(n));
}
@Override
public Placeholders getPlaceholders(GuildViewInventory inv, int n) {
UUID uuid = inv.members.get(n);
Placeholders holders = new Placeholders();
/*
* Will never be null since a players name will always be recorded
* if they've been in a guild
*/
holders.register("name", Bukkit.getOfflinePlayer(uuid).getName());
OfflinePlayer player = getEffectivePlayer(inv, n);
Placeholders holders = new Placeholders();
OfflinePlayerData offline = OfflinePlayerData.get(uuid);
holders.register("name", MMOCoreUtils.isInvalid(player) ? "???" : player.getName());
holders.register("class", offline.getProfess().getName());
holders.register("level", offline.getLevel());
holders.register("since", new DelayFormat(2).format(System.currentTimeMillis() - offline.getLastLogin()));
return holders;
}
@ -75,7 +80,7 @@ public class EditableGuildView extends EditableInventory {
meta.getPersistentDataContainer().set(UUID_NAMESPACEDKEY, PersistentDataType.STRING, uuid.toString());
if (meta instanceof SkullMeta)
inv.dynamicallyUpdateItem(this, n, disp, current -> {
inv.asyncUpdate(this, n, disp, current -> {
((SkullMeta) meta).setOwningPlayer(Bukkit.getOfflinePlayer(uuid));
current.setItemMeta(meta);
});

View File

@ -57,7 +57,7 @@ public class EditablePartyView extends EditableInventory {
if (member.isOnline())
holders.register("name", member.getPlayer().getName());
holders.register("class", member.getProfess().getName());
holders.register("level", "" + member.getLevel());
holders.register("level", member.getLevel());
holders.register("since", new DelayFormat(2).format(System.currentTimeMillis() - member.getLastLogin()));
return holders;
}
@ -77,7 +77,7 @@ public class EditablePartyView extends EditableInventory {
meta.getPersistentDataContainer().set(UUID_NAMESPACEDKEY, PersistentDataType.STRING, member.getUniqueId().toString());
if (meta instanceof SkullMeta)
inv.dynamicallyUpdateItem(this, n, disp, current -> {
inv.asyncUpdate(this, n, disp, current -> {
((SkullMeta) meta).setOwningPlayer(member);
current.setItemMeta(meta);
});