Files
Utilities-OG/src/main/java/net/trueog/utilitiesog/UtilitiesOG.java
T
SKBotNL 0a02285270 Fix item slots and use CraftBukkit and NMS APIs to deserialize slot NBT tags
By using the CraftBukkit and NMS APIs any additional NBT ("tag") data doesn't get lost
2026-09-16 18:45:45 +02:00

580 lines
18 KiB
Java

// This is free and unencumbered software released into the public domain.
// Authors: christianniehaus, NotAlexNoyle.
package net.trueog.utilitiesog;
import java.io.File;
import java.util.List;
import java.util.UUID;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.trueog.utilitiesog.utils.PlayerDataUtils;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.java.JavaPlugin;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.LuckPermsProvider;
import net.luckperms.api.model.group.Group;
import net.luckperms.api.model.user.User;
import net.luckperms.api.node.types.PrefixNode;
import net.trueog.utilitiesog.commands.AboutCommand;
import net.trueog.utilitiesog.commands.BingCommand;
import net.trueog.utilitiesog.commands.ColorCodesCommand;
import net.trueog.utilitiesog.commands.PingCommand;
import net.trueog.utilitiesog.commands.RanksCommand;
import net.trueog.utilitiesog.commands.ToggleCrammingCommand;
import net.trueog.utilitiesog.commands.TogglePhantomsCommand;
import net.trueog.utilitiesog.commands.missingcommands.FCommand;
import net.trueog.utilitiesog.commands.missingcommands.FactionCommand;
import net.trueog.utilitiesog.commands.missingcommands.GuildCommand;
import net.trueog.utilitiesog.commands.missingcommands.KitCommand;
import net.trueog.utilitiesog.commands.missingcommands.RTPCommand;
import net.trueog.utilitiesog.commands.missingcommands.SeedCommand;
import net.trueog.utilitiesog.commands.missingcommands.WildCommand;
import net.trueog.utilitiesog.listeners.AdvancementsOnlyInSurvivalListener;
import net.trueog.utilitiesog.listeners.DisableEntityCrammingListener;
import net.trueog.utilitiesog.listeners.NoFlippyListener;
import net.trueog.utilitiesog.listeners.PhantomState;
import net.trueog.utilitiesog.listeners.TogglePhantomsListener;
import net.trueog.utilitiesog.misc.FlagRegistrationException;
import net.trueog.utilitiesog.modules.ChainArmorModule;
import net.trueog.utilitiesog.modules.MockBambooModule;
import net.trueog.utilitiesog.modules.RandomDropsModule;
import net.trueog.utilitiesog.utils.MessageFormat;
import net.trueog.utilitiesog.utils.PlaceholderUtils;
import net.trueog.utilitiesog.utils.TextUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
// Declare main plugin class.
public final class UtilitiesOG extends JavaPlugin {
// Formatted plugin prefix used on every console log line emitted from
// Utilities-OG API. Reached internally via Internal.getPrefix(),
private static final String PREFIX = "&7[&6Utilities&f-&4OG&7] ";
// Declare live plugin instance to be initialized in onEnable().
private static UtilitiesOG instance;
private static PlayerDataUtils playerDataUtils;
// Package-private hooks consumed by net.trueog.utilitiesog.Internal.
static UtilitiesOG getPluginInstance() {
return instance;
}
static String getPluginPrefix() {
return PREFIX;
}
@Override
public void onEnable() {
instance = this;
playerDataUtils = new PlayerDataUtils();
final File configFile = new File(this.getDataFolder(), "config.yml");
if (!configFile.exists()) {
this.saveDefaultConfig();
}
if (this.getConfig().getBoolean("ChainArmor")) {
ChainArmorModule.Enable();
}
if (this.getConfig().getBoolean("ColorCodes")) {
this.getCommand("colorcodes").setExecutor(new ColorCodesCommand());
}
if (this.getConfig().getBoolean("DisableEntityCramming")) {
getServer().getPluginManager().registerEvents(new DisableEntityCrammingListener(), this);
this.getCommand("togglecramming").setExecutor(new ToggleCrammingCommand());
}
// Registering a global MiniPlaceholder.
registerGlobalPlaceholder("servers_name", () -> "&aTrue&cOG &eNetwork");
// Registering an Audience MiniPlaceholder.
registerAudiencePlaceholder("player_display_name", (Player player) -> {
final LuckPerms luckPerms = LuckPermsProvider.get();
final User user = luckPerms.getUserManager().getUser(player.getUniqueId());
if (user == null) {
getLogger().info("ERROR: MiniPlaceholder processing error. Player: " + player.getName()
+ " has no User object in LuckPerms!");
return player.getName();
}
final String primaryGroup = user.getPrimaryGroup();
final Group group = luckPerms.getGroupManager().getGroup(primaryGroup);
if (group == null) {
getLogger().info("ERROR: MiniPlaceholder processing error. User: " + player.getName()
+ " has no Group assignment in LuckPerms!");
return player.getName();
}
final String prefix = group.getNodes().stream().filter(node -> node instanceof PrefixNode).findFirst()
.map(node -> ((PrefixNode) node).getMetaValue()).orElse(null);
if (prefix == null) {
return player.getName();
}
final String colorCode = prefix.replaceAll(".*\\](.*)", "$1");
return "<luckperms_prefix>" + colorCode + " " + player.getName();
});
if (this.getConfig().getBoolean("MockBamboo")) {
MockBambooModule.Enable();
}
if (this.getConfig().getBoolean("NoFlippy")) {
if (getServer().getPluginManager().getPlugin("WorldGuard") != null) {
getServer().getPluginManager().registerEvents(new NoFlippyListener(), this);
} else {
this.getLogger().severe("WorldGuard is not installed! Disabling NoFlippy...");
}
}
RandomDropsModule.enable(this);
if (this.getConfig().getBoolean("Ping")) {
this.getCommand("ping").setExecutor(new PingCommand());
this.getCommand("bing").setExecutor(new BingCommand());
}
if (this.getConfig().getBoolean("RanksMenu")) {
this.getCommand("ranks").setExecutor(new RanksCommand());
}
if (this.getConfig().getBoolean("TogglePhantoms")) {
PhantomState.load(this);
getServer().getPluginManager().registerEvents(new TogglePhantomsListener(), this);
this.getCommand("togglephantoms").setExecutor(new TogglePhantomsCommand());
}
if (this.getConfig().getBoolean("AdvancementsOnlyInSurvival")) {
getServer().getPluginManager().registerEvents(new AdvancementsOnlyInSurvivalListener(), this);
}
if (this.getConfig().getBoolean("MissingCommands")) {
this.getCommand("wild").setExecutor(new WildCommand());
this.getCommand("rtp").setExecutor(new RTPCommand());
this.getCommand("seed").setExecutor(new SeedCommand());
this.getCommand("f").setExecutor(new FCommand());
this.getCommand("faction").setExecutor(new FactionCommand());
this.getCommand("guild").setExecutor(new GuildCommand());
this.getCommand("kit").setExecutor(new KitCommand());
}
this.getCommand("utilities").setExecutor(new AboutCommand());
}
@Override
public void onLoad() {
try {
NoFlippyListener.registerFlag();
} catch (FlagRegistrationException error) {
this.getLogger()
.severe("ERROR: Failed to register the can-flippy Flag with WorldGuard. " + error.getMessage());
}
}
@Override
public void onDisable() {
PlaceholderUtils.unregisterAll();
}
// Sends a fully formatted message (every supported tag active).
public static void trueogMessage(Player player, String message) {
TextUtils.trueogMessage(player, message);
}
// Sends a selectively formatted message. Disabled tags stay as literal
// MiniMessage
// markup in the rendered output.
public static void trueogMessage(Player player, String message, MessageFormat format) {
TextUtils.trueogMessage(player, message, format);
}
// Sends a fully formatted message (every supported tag active) without
// expanding MiniPlaceholders.
public static void trueogRawMessage(Player player, String message) {
TextUtils.trueogRawMessage(player, message);
}
// Sends a selectively formatted message without expanding MiniPlaceholders.
public static void trueogRawMessage(Player player, String message, MessageFormat format) {
TextUtils.trueogRawMessage(player, message, format);
}
// Sends a pre-built Component to a player on the caller's thread.
public static void trueogMessage(Player player, Component message) {
TextUtils.trueogMessage(player, message);
}
// Sends a pre-built Component after selectively formatting.
// Rainbow and gradient are indistinguishable from color at the
// Component level, so those flags have no effect here beyond what color() does.
public static void trueogMessage(Player player, Component message, MessageFormat format) {
TextUtils.trueogMessage(player, message, format);
}
// Handle messages to players based on UUID. Useful when the caller doesn't have
// a Player object on hand.
public static void trueogMessage(UUID playerUUID, String message) {
trueogMessage(playerUUID, message, MessageFormat.full());
}
// Format-aware UUID based message send.
public static void trueogMessage(UUID playerUUID, String message, MessageFormat format) {
final Player player = Bukkit.getPlayer(playerUUID);
if (player != null) {
TextUtils.trueogMessage(player, message, format);
} else {
logToConsole("[Utilities-OG]", "Player with UUID " + playerUUID + " is not online.");
}
}
// Handle non-expanded messages to offline players (based on UUID).
public static void trueogRawMessage(UUID playerUUID, String message) {
trueogRawMessage(playerUUID, message, MessageFormat.full());
}
// Format-aware UUID raw send.
public static void trueogRawMessage(UUID playerUUID, String message, MessageFormat format) {
final Player player = Bukkit.getPlayer(playerUUID);
if (player != null) {
TextUtils.trueogRawMessage(player, message, format);
} else {
logToConsole("[Utilities-OG]", "Player with UUID " + playerUUID + " is not online.");
}
}
// Formats a message without MiniPlaceholder expansion, every tag active.
public static TextComponent trueogColorize(String message) {
return trueogColorize(message, MessageFormat.full());
}
// Format-aware colorize. Disabled tags stay literal in the output.
public static TextComponent trueogColorize(String message, MessageFormat format) {
return (TextComponent) TextUtils.miniMessage(format).deserialize(TextUtils.processColorCodes(message));
}
// Expands Global MiniPlaceholders with every tag active.
public static TextComponent trueogExpand(String message) {
return TextUtils.expandTextWithPlaceholders(message);
}
// Format-aware global expansion.
public static TextComponent trueogExpand(String message, MessageFormat format) {
return TextUtils.expandTextWithPlaceholders(message, format);
}
// Expands Audience MiniPlaceholders with every tag active.
public static TextComponent trueogExpand(String message, Player player) {
return TextUtils.expandTextWithPlaceholders(message, player);
}
// Format-aware audience expansion.
public static TextComponent trueogExpand(String message, Player player, MessageFormat format) {
return TextUtils.expandTextWithPlaceholders(message, player, format);
}
// Expands Relational MiniPlaceholders with every tag active.
public static TextComponent trueogExpand(String message, Player player, Player target) {
return TextUtils.expandTextWithPlaceholders(message, player, target);
}
// Format-aware relational expansion.
public static TextComponent trueogExpand(String message, Player player, Player target, MessageFormat format) {
return TextUtils.expandTextWithPlaceholders(message, player, target, format);
}
// Variation of MiniPlaceholder expansion for when Bukkit API is unavailable.
public static TextComponent trueogExpand(String message, UUID playerUUID) {
return trueogExpand(message, playerUUID, MessageFormat.full());
}
// Format-aware UUID expansion. Falls back to global context when the
// player is offline.
public static TextComponent trueogExpand(String message, UUID playerUUID, MessageFormat format) {
final Player player = Bukkit.getPlayer(playerUUID);
if (player != null) {
return TextUtils.expandTextWithPlaceholders(message, player, format);
} else {
logToConsole("[Utilities-OG]", "Player with UUID " + playerUUID + " is not online.");
return TextUtils.expandTextWithPlaceholders(message, format);
}
}
// Global placeholder without arguments.
public static void registerGlobalPlaceholder(String name, Supplier<String> valueSupplier) {
PlaceholderUtils.trueogRegisterMiniPlaceholder(name,
placeholder -> placeholder.setGlobalPlaceholder(valueSupplier));
}
// Global placeholder with arguments.
public static void registerGlobalPlaceholder(String name, Function<List<String>, String> valueFunction) {
PlaceholderUtils.trueogRegisterMiniPlaceholder(name,
placeholder -> placeholder.setGlobalPlaceholder(valueFunction));
}
// Audience placeholder without arguments.
public static void registerAudiencePlaceholder(String name, Function<Player, String> valueFunction) {
PlaceholderUtils.trueogRegisterMiniPlaceholder(name,
placeholder -> placeholder.setAudiencePlaceholder(valueFunction));
}
// Audience placeholder with arguments.
public static void registerAudiencePlaceholder(String name,
BiFunction<Player, List<String>, String> valueFunction)
{
PlaceholderUtils.trueogRegisterMiniPlaceholder(name,
placeholder -> placeholder.setAudiencePlaceholder(valueFunction));
}
// Relational placeholder without arguments.
public static void registerRelationalPlaceholder(String name, BiFunction<Player, Player, String> valueFunction) {
PlaceholderUtils.trueogRegisterMiniPlaceholder(name,
placeholder -> placeholder.setRelationalPlaceholder(valueFunction));
}
// Relational placeholder with arguments.
public static void registerRelationalPlaceholder(String name,
Internal.TriFunction<Player, Player, List<String>, String> valueFunction)
{
PlaceholderUtils.trueogRegisterMiniPlaceholder(name,
placeholder -> placeholder.setRelationalPlaceholder(valueFunction));
}
// Strips every legacy Bukkit code and every supported MiniMessage tag,
// returning plain text.
public static String stripFormatting(String content) {
return TextUtils.stripFormatting(content);
}
// Flattens a Component to plain text, discarding every style and tag.
public static String stripFormatting(Component component) {
return TextUtils.stripFormatting(component);
}
// Console logging API that strips every supported tag and legacy code.
public static void logToConsole(String prefix, String message) {
Bukkit.getLogger().info(TextUtils.stripFormatting(prefix + " " + message));
}
public static @Nullable ItemStack @NotNull [] getInventoryData(UUID uuid) {
final ListTag inventoryData = playerDataUtils.getPlayerData(uuid).getList("Inventory", Tag.TAG_COMPOUND);
final ItemStack[] inventoryContents = new ItemStack[41];
for (int i = 0; i < inventoryData.size(); i++) {
final CompoundTag slotTag = inventoryData.getCompound(i);
final byte slot = slotTag.getByte("Slot");
final ItemStack itemStack = CraftItemStack.asBukkitCopy(net.minecraft.world.item.ItemStack.of(slotTag));
if (slot >= 0 && slot <= 35) {
inventoryContents[slot] = itemStack;
} else if (slot >= 100 && slot <= 103) {
inventoryContents[slot - 64] = itemStack;
} else if (slot == -106) {
inventoryContents[40] = itemStack;
}
}
return inventoryContents;
}
public static void setInventoryData(UUID uuid, @Nullable ItemStack @NotNull [] items) {
final ListTag inventoryData = new ListTag();
for (byte i = 0; i < items.length; i++) {
final ItemStack item = items[i];
if (item == null) {
continue;
}
final byte slot;
if (i <= 35) {
slot = i;
} else if (i <= 39) {
slot = (byte) (i + 64);
} else if (i == 40) {
slot = -106;
} else {
continue;
}
CompoundTag slotTag = new CompoundTag();
CraftItemStack.asNMSCopy(item).save(slotTag);
slotTag.putByte("Slot", slot);
inventoryData.add(slotTag);
}
playerDataUtils.setInventoryData(uuid, inventoryData);
}
public static int getHeldItemSlot(UUID uuid) {
return playerDataUtils.getPlayerData(uuid).getInt("SelectedItemSlot");
}
}