By using the CraftBukkit and NMS APIs any additional NBT ("tag") data doesn't get lost
580 lines
18 KiB
Java
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");
|
|
|
|
}
|
|
|
|
}
|