From 025bba917be1efbdf96505c3c4fc70eb15f69387 Mon Sep 17 00:00:00 2001 From: Xiao-MoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Tue, 21 Jun 2022 22:43:16 +0800 Subject: [PATCH] 1.0 - SNAPSHOT --- .../customnameplates/AdventureManager.java | 24 ++ .../customnameplates/ConfigManager.java | 157 ++++++++++ .../customnameplates/CustomNameplates.java | 80 +++++ .../customnameplates/HookManager.java | 54 ++++ .../customnameplates/commands/Execute.java | 294 ++++++++++++++++++ .../commands/TabComplete.java | 86 +++++ .../customnameplates/data/DataManager.java | 58 ++++ .../customnameplates/data/PlayerData.java | 35 +++ .../customnameplates/data/SqlHandler.java | 132 ++++++++ .../customnameplates/font/FontCache.java | 86 +++++ .../customnameplates/font/FontChar.java | 43 +++ .../customnameplates/font/FontNegative.java | 97 ++++++ .../customnameplates/font/FontWidth.java | 78 +++++ .../listener/PacketsListener.java | 55 ++++ .../listener/PlayerListener.java | 40 +++ .../nameplates/NameplateConfig.java | 27 ++ .../nameplates/NameplateUtil.java | 57 ++++ .../resource/ResourceManager.java | 237 ++++++++++++++ .../scoreboard/NameplatesTeam.java | 89 ++++++ .../scoreboard/ScoreBoardManager.java | 46 +++ .../customnameplates/utils/SqlConnection.java | 184 +++++++++++ src/main/resources/config.yml | 47 +++ src/main/resources/database.yml | 28 ++ src/main/resources/messages/messages_cn.yml | 16 + src/main/resources/messages/messages_en.yml | 16 + src/main/resources/plugin.yml | 17 + src/main/resources/resources/cat.png | Bin 0 -> 812 bytes src/main/resources/resources/cheems.png | Bin 0 -> 820 bytes src/main/resources/resources/egg.png | Bin 0 -> 769 bytes src/main/resources/resources/wither.png | Bin 0 -> 636 bytes src/main/resources/space_split.png | Bin 0 -> 336 bytes 31 files changed, 2083 insertions(+) create mode 100644 src/main/java/net/momirealms/customnameplates/AdventureManager.java create mode 100644 src/main/java/net/momirealms/customnameplates/ConfigManager.java create mode 100644 src/main/java/net/momirealms/customnameplates/CustomNameplates.java create mode 100644 src/main/java/net/momirealms/customnameplates/HookManager.java create mode 100644 src/main/java/net/momirealms/customnameplates/commands/Execute.java create mode 100644 src/main/java/net/momirealms/customnameplates/commands/TabComplete.java create mode 100644 src/main/java/net/momirealms/customnameplates/data/DataManager.java create mode 100644 src/main/java/net/momirealms/customnameplates/data/PlayerData.java create mode 100644 src/main/java/net/momirealms/customnameplates/data/SqlHandler.java create mode 100644 src/main/java/net/momirealms/customnameplates/font/FontCache.java create mode 100644 src/main/java/net/momirealms/customnameplates/font/FontChar.java create mode 100644 src/main/java/net/momirealms/customnameplates/font/FontNegative.java create mode 100644 src/main/java/net/momirealms/customnameplates/font/FontWidth.java create mode 100644 src/main/java/net/momirealms/customnameplates/listener/PacketsListener.java create mode 100644 src/main/java/net/momirealms/customnameplates/listener/PlayerListener.java create mode 100644 src/main/java/net/momirealms/customnameplates/nameplates/NameplateConfig.java create mode 100644 src/main/java/net/momirealms/customnameplates/nameplates/NameplateUtil.java create mode 100644 src/main/java/net/momirealms/customnameplates/resource/ResourceManager.java create mode 100644 src/main/java/net/momirealms/customnameplates/scoreboard/NameplatesTeam.java create mode 100644 src/main/java/net/momirealms/customnameplates/scoreboard/ScoreBoardManager.java create mode 100644 src/main/java/net/momirealms/customnameplates/utils/SqlConnection.java create mode 100644 src/main/resources/config.yml create mode 100644 src/main/resources/database.yml create mode 100644 src/main/resources/messages/messages_cn.yml create mode 100644 src/main/resources/messages/messages_en.yml create mode 100644 src/main/resources/plugin.yml create mode 100644 src/main/resources/resources/cat.png create mode 100644 src/main/resources/resources/cheems.png create mode 100644 src/main/resources/resources/egg.png create mode 100644 src/main/resources/resources/wither.png create mode 100644 src/main/resources/space_split.png diff --git a/src/main/java/net/momirealms/customnameplates/AdventureManager.java b/src/main/java/net/momirealms/customnameplates/AdventureManager.java new file mode 100644 index 0000000..06d6390 --- /dev/null +++ b/src/main/java/net/momirealms/customnameplates/AdventureManager.java @@ -0,0 +1,24 @@ +package net.momirealms.customnameplates; + +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +public class AdventureManager { + //发送控制台消息 + public static void consoleMessage(String s) { + Audience au = CustomNameplates.adventure.sender(Bukkit.getConsoleSender()); + MiniMessage mm = MiniMessage.miniMessage(); + Component parsed = mm.deserialize(s); + au.sendMessage(parsed); + } + //发送玩家消息 + public static void playerMessage(Player player, String s){ + Audience au = CustomNameplates.adventure.player(player); + MiniMessage mm = MiniMessage.miniMessage(); + Component parsed = mm.deserialize(s); + au.sendMessage(parsed); + } +} diff --git a/src/main/java/net/momirealms/customnameplates/ConfigManager.java b/src/main/java/net/momirealms/customnameplates/ConfigManager.java new file mode 100644 index 0000000..ea8c7ae --- /dev/null +++ b/src/main/java/net/momirealms/customnameplates/ConfigManager.java @@ -0,0 +1,157 @@ +package net.momirealms.customnameplates; + +import net.kyori.adventure.key.Key; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; + +public class ConfigManager { + + //根据文件名获取配置文件 + public static YamlConfiguration getConfig(String configName) { + File file = new File(CustomNameplates.instance.getDataFolder(), configName); + //文件不存在则生成默认配置 + if (!file.exists()) { + CustomNameplates.instance.saveResource(configName, false); + } + return YamlConfiguration.loadConfiguration(file); + } + //主配置文件 + public static class MainConfig{ + + public static String namespace; + public static String fontName; + public static String start_char; + public static String folder_path; + public static String font; + public static String default_nameplate; + public static String player_prefix; + public static String player_suffix; + public static Key key; + public static boolean itemsAdder; + public static boolean placeholderAPI; + public static boolean show_after; + public static String lang; + public static Long preview; + + public static void ReloadConfig(){ + CustomNameplates.instance.saveDefaultConfig(); + CustomNameplates.instance.reloadConfig(); + FileConfiguration config = CustomNameplates.instance.getConfig(); + + lang = config.getString("config.lang"); + namespace = config.getString("config.namespace"); + font = config.getString("config.font"); + fontName = namespace + ":" + font; + start_char = config.getString("config.start-char"); + folder_path = config.getString("config.folder-path"); + default_nameplate = config.getString("config.default-nameplate"); + player_prefix = config.getString("config.prefix"); + player_suffix = config.getString("config.suffix"); + itemsAdder =config.getBoolean("config.integrations.ItemsAdder"); + placeholderAPI = config.getBoolean("config.integrations.PlaceholderAPI"); + show_after = config.getBoolean("config.show-after-load-resourcepack"); + key = Key.key(fontName); + preview = config.getLong("config.preview-duration"); + } + } + //消息文件 + public static class Message{ + + public static String noPerm; + public static String prefix; + public static String lackArgs; + public static String reload; + public static String equip; + public static String unequip; + public static String force_equip; + public static String force_unequip; + public static String not_exist; + public static String not_online; + public static String no_console; + public static String notAvailable; + public static String available; + public static String cooldown; + public static String preview; + + public static void ReloadConfig(){ + YamlConfiguration messagesConfig = getConfig("messages/messages_" + MainConfig.lang +".yml"); + noPerm = messagesConfig.getString("messages.no-perm"); + prefix = messagesConfig.getString("messages.prefix"); + lackArgs = messagesConfig.getString("messages.lack-args"); + reload = messagesConfig.getString("messages.reload"); + equip = messagesConfig.getString("messages.equip"); + unequip = messagesConfig.getString("messages.unequip"); + force_equip = messagesConfig.getString("messages.force-equip"); + force_unequip = messagesConfig.getString("messages.force-unequip"); + not_exist = messagesConfig.getString("messages.not-exist"); + not_online = messagesConfig.getString("messages.not-online"); + no_console = messagesConfig.getString("messages.no-console"); + notAvailable = messagesConfig.getString("messages.not-available"); + available = messagesConfig.getString("messages.available"); + cooldown = messagesConfig.getString("messages.cooldown"); + preview = messagesConfig.getString("messages.preview"); + } + } + //数据库配置 + public static class DatabaseConfig{ + + public static String user; + public static String password; + public static String url; + public static String ENCODING; + public static String tableName; + public static boolean enable_pool; + public static boolean use_mysql; + public static int maximum_pool_size; + public static int minimum_idle; + public static int maximum_lifetime; + public static int idle_timeout; + + public static void LoadConfig(){ + YamlConfiguration databaseConfig = getConfig("database.yml"); + String storage_mode = databaseConfig.getString("settings.storage-mode"); + + //使用SQLite + if(storage_mode.equals("SQLite")){ + enable_pool = false; + use_mysql = false; + tableName = "nameplates"; + } + //使用MYSQL + else if(storage_mode.equals("MYSQL")){ + + use_mysql = true; + ENCODING = databaseConfig.getString("MySQL.property.encoding"); + tableName = databaseConfig.getString("MySQL.table-name"); + user = databaseConfig.getString("MySQL.user"); + password = databaseConfig.getString("MySQL.password"); + + url = "jdbc:mysql://" + databaseConfig.getString("MySQL.host") + + ":" + databaseConfig.getString("MySQL.port") + "/" + + databaseConfig.getString("MySQL.database") + "?characterEncoding=" + + ENCODING + "&useSSL=" + + databaseConfig.getString("MySQL.property.use-ssl"); + if (databaseConfig.getString("MySQL.property.timezone") != null && + !databaseConfig.getString("MySQL.property.timezone").equals("")) { + url = url + "&serverTimezone=" + databaseConfig.getString("MySQL.property.timezone"); + } + if (databaseConfig.getBoolean("MySQL.property.allowPublicKeyRetrieval")) { + url = url + "&allowPublicKeyRetrieval=true"; + } + enable_pool = databaseConfig.getBoolean("settings.use-pool"); + if(enable_pool){ + maximum_pool_size = databaseConfig.getInt("Pool-Settings.maximum-pool-size"); + minimum_idle = databaseConfig.getInt("Pool-Settings.minimum-idle"); + maximum_lifetime = databaseConfig.getInt("Pool-Settings.maximum-lifetime"); + idle_timeout = databaseConfig.getInt("Pool-Settings.idle-timeout"); + } + }else { + AdventureManager.consoleMessage("这种存储方式不存在"); + Bukkit.getPluginManager().disablePlugin(CustomNameplates.instance); + } + } + } +} diff --git a/src/main/java/net/momirealms/customnameplates/CustomNameplates.java b/src/main/java/net/momirealms/customnameplates/CustomNameplates.java new file mode 100644 index 0000000..206ad8d --- /dev/null +++ b/src/main/java/net/momirealms/customnameplates/CustomNameplates.java @@ -0,0 +1,80 @@ +package net.momirealms.customnameplates; + +import com.comphenix.protocol.ProtocolLibrary; +import net.kyori.adventure.platform.bukkit.BukkitAudiences; +import net.momirealms.customnameplates.commands.Execute; +import net.momirealms.customnameplates.commands.TabComplete; +import net.momirealms.customnameplates.data.DataManager; +import net.momirealms.customnameplates.data.SqlHandler; +import net.momirealms.customnameplates.listener.PlayerListener; +import net.momirealms.customnameplates.listener.PacketsListener; +import net.momirealms.customnameplates.resource.ResourceManager; +import net.momirealms.customnameplates.scoreboard.ScoreBoardManager; +import org.bukkit.Bukkit; +import org.bukkit.entity.Entity; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.Objects; + +public final class CustomNameplates extends JavaPlugin { + + public static JavaPlugin instance; + public static BukkitAudiences adventure; + + private ResourceManager resourceManager; + private DataManager dataManager; + private HookManager hookManager; + private ScoreBoardManager scoreBoardManager; + + public ResourceManager getResourceManager() { + return this.resourceManager; + } + public DataManager getDataManager() { return this.dataManager; } + public HookManager getHookManager() { return this.hookManager; } + public ScoreBoardManager getScoreBoardManager() { return this.scoreBoardManager; } + + @Override + public void onEnable() { + instance = this; + adventure = BukkitAudiences.create(this); + //重载插件 + ConfigManager.MainConfig.ReloadConfig(); + ConfigManager.Message.ReloadConfig(); + ConfigManager.DatabaseConfig.LoadConfig(); + //指令注册 + Objects.requireNonNull(Bukkit.getPluginCommand("customnameplates")).setExecutor(new Execute(this)); + Objects.requireNonNull(Bukkit.getPluginCommand("customnameplates")).setTabCompleter(new TabComplete(this)); + //事件注册 + Bukkit.getPluginManager().registerEvents(new PlayerListener(this),this); + ProtocolLibrary.getProtocolManager().addPacketListener(new PacketsListener(this)); + //新建单例 + this.resourceManager = new ResourceManager(this); + this.dataManager = new DataManager(); + this.hookManager = new HookManager(this); + this.scoreBoardManager = new ScoreBoardManager(this); + //生成资源包 + resourceManager.generateResourcePack(); + if (!DataManager.create()) { + AdventureManager.consoleMessage("[CustomNameplates] Error! Failed to enable Data Manager! Disabling plugin..."); + instance.getPluginLoader().disablePlugin(instance); + return; + } + //启动完成 + AdventureManager.consoleMessage("[CustomNameplates] Plugin has been enabled! Author: XiaoMoMi"); + } + + @Override + public void onDisable() { + SqlHandler.saveAll(); + SqlHandler.close(); + //清除缓存实体 + Execute.pCache.forEach(Entity::remove); + //卸载完成 + AdventureManager.consoleMessage("[CustomNameplates] Plugin has been disabled! Author: XiaoMoMi"); + //关闭adventure + if(adventure != null) { + adventure.close(); + adventure = null; + } + } +} diff --git a/src/main/java/net/momirealms/customnameplates/HookManager.java b/src/main/java/net/momirealms/customnameplates/HookManager.java new file mode 100644 index 0000000..56a89c6 --- /dev/null +++ b/src/main/java/net/momirealms/customnameplates/HookManager.java @@ -0,0 +1,54 @@ +package net.momirealms.customnameplates; + +import me.clip.placeholderapi.PlaceholderAPI; +import org.apache.commons.lang.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; + +public class HookManager { + + private boolean placeholderAPI; + private boolean itemsAdder; + + public boolean hasPlaceholderAPI() { + return this.placeholderAPI; + } + public boolean hasItemsAdder() { + return this.itemsAdder; + } + + public HookManager(CustomNameplates plugin) { + this.initializePlaceholderAPI(); + this.initializeItemsAdder(); + } + + //Papi Hook检测 + private void initializePlaceholderAPI() { + if(!ConfigManager.MainConfig.placeholderAPI){ + this.placeholderAPI = false; + return; + } + if(CustomNameplates.instance.getServer().getPluginManager().getPlugin("PlaceholderAPI") != null){ + this.placeholderAPI = true; + AdventureManager.consoleMessage("[CustomNameplates] " + "PlaceholderAPI Hooked!"); + } + } + //ItemsAdder Hook检测 + private void initializeItemsAdder() { + if (!ConfigManager.MainConfig.itemsAdder) { + this.itemsAdder = false; + } + if(CustomNameplates.instance.getServer().getPluginManager().getPlugin("ItemsAdder") != null){ + this.itemsAdder = true; + AdventureManager.consoleMessage("[CustomNameplates] " + "ItemsAdder Hooked!"); + } + } + /* + 解析prefix与suffix + */ + public String parsePlaceholders(Player player, String papi) { + String s = StringUtils.replace(StringUtils.replace(papi, "%player_name%", player.getName()), "%player_displayname%", player.getDisplayName()); + s = PlaceholderAPI.setPlaceholders(player, s); + return ChatColor.translateAlternateColorCodes('&', s); + } +} \ No newline at end of file diff --git a/src/main/java/net/momirealms/customnameplates/commands/Execute.java b/src/main/java/net/momirealms/customnameplates/commands/Execute.java new file mode 100644 index 0000000..cd98fc6 --- /dev/null +++ b/src/main/java/net/momirealms/customnameplates/commands/Execute.java @@ -0,0 +1,294 @@ +package net.momirealms.customnameplates.commands; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.wrappers.WrappedChatComponent; +import com.comphenix.protocol.wrappers.WrappedDataWatcher; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.momirealms.customnameplates.ConfigManager; +import net.momirealms.customnameplates.AdventureManager; +import net.momirealms.customnameplates.CustomNameplates; +import net.momirealms.customnameplates.data.DataManager; +import net.momirealms.customnameplates.scoreboard.NameplatesTeam; +import org.apache.commons.lang.StringUtils; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.permissions.PermissionAttachmentInfo; +import org.bukkit.scheduler.BukkitScheduler; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Optional; + +public class Execute implements CommandExecutor { + + private final CustomNameplates plugin; + private final HashMap coolDown; + { + coolDown = new HashMap<>(); + } + public static List pCache; + { + pCache = new ArrayList<>(); + } + + public Execute(CustomNameplates plugin) { + this.plugin = plugin; + } + + @Override + @ParametersAreNonnullByDefault + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + //参数不足 + if (args.length < 1){ + if (sender instanceof Player){ + AdventureManager.playerMessage((Player) sender,ConfigManager.Message.prefix + ConfigManager.Message.lackArgs); + }else { + AdventureManager.consoleMessage(ConfigManager.Message.prefix + ConfigManager.Message.lackArgs); + } + return true; + } + switch (args[0]) { + case "reload" -> { + if (sender.hasPermission("customnameplates.reload") || sender.isOp()) { + ConfigManager.MainConfig.ReloadConfig(); + ConfigManager.Message.ReloadConfig(); + if (sender instanceof Player) { + AdventureManager.playerMessage((Player) sender, ConfigManager.Message.prefix + ConfigManager.Message.reload); + } else { + AdventureManager.consoleMessage(ConfigManager.Message.prefix + ConfigManager.Message.reload); + } + } else { + AdventureManager.playerMessage((Player) sender, ConfigManager.Message.prefix + ConfigManager.Message.noPerm); + } + return true; + } + case "equip" -> { + if (sender instanceof Player player) { + if (args.length < 2) { + AdventureManager.playerMessage((Player) sender, ConfigManager.Message.prefix + ConfigManager.Message.lackArgs); + return true; + } + if (sender.hasPermission("customnameplates.equip." + args[1]) || sender.isOp()) { + if (plugin.getResourceManager().getNameplateInfo(args[1]) == null) { + AdventureManager.playerMessage((Player) sender, ConfigManager.Message.prefix + ConfigManager.Message.not_exist); + return true; + } + DataManager.cache.get(player.getUniqueId()).equipNameplate(args[1]); + this.plugin.getScoreBoardManager().getTeam(player.getName()).updateNameplates(); + this.plugin.getDataManager().savePlayer(player.getUniqueId()); + AdventureManager.playerMessage((Player) sender, ConfigManager.Message.prefix + ConfigManager.Message.equip.replace("{Nameplate}", plugin.getResourceManager().getNameplateInfo(args[1]).getConfig().getName())); + } else { + AdventureManager.playerMessage((Player) sender, ConfigManager.Message.prefix + ConfigManager.Message.notAvailable); + } + } else { + AdventureManager.consoleMessage(ConfigManager.Message.prefix + ConfigManager.Message.no_console); + } + return true; + } + case "forceequip" -> { + if (args.length < 3){ + if(sender instanceof Player){ + AdventureManager.playerMessage((Player) sender,ConfigManager.Message.prefix + ConfigManager.Message.lackArgs); + }else { + AdventureManager.consoleMessage(ConfigManager.Message.prefix + ConfigManager.Message.lackArgs); + } + return true; + } + if (sender.hasPermission("customnameplates.forceequip") || sender.isOp()){ + if (Bukkit.getPlayer(args[1]) != null){ + Player player = Bukkit.getPlayer(args[1]); + //铭牌是否存在 + if (plugin.getResourceManager().getNameplateInfo(args[2]) == null){ + if(sender instanceof Player){ + AdventureManager.playerMessage((Player) sender,ConfigManager.Message.prefix + ConfigManager.Message.not_exist); + }else { + AdventureManager.consoleMessage(ConfigManager.Message.prefix + ConfigManager.Message.not_exist); + } + return true; + } + DataManager.cache.get(player.getUniqueId()).equipNameplate(args[2]); + this.plugin.getScoreBoardManager().getTeam(args[1]).updateNameplates(); + this.plugin.getDataManager().savePlayer(player.getUniqueId()); + if (sender instanceof Player){ + AdventureManager.playerMessage((Player) sender, ConfigManager.Message.prefix + ConfigManager.Message.force_equip.replace("{Nameplate}", plugin.getResourceManager().getNameplateInfo(args[2]).getConfig().getName()).replace("{Player}", args[1])); + }else { + AdventureManager.consoleMessage(ConfigManager.Message.prefix + ConfigManager.Message.force_equip.replace("{Nameplate}", plugin.getResourceManager().getNameplateInfo(args[2]).getConfig().getName()).replace("{Player}", args[1])); + } + }else { + //玩家不存在,不在线 + if(sender instanceof Player){ + AdventureManager.playerMessage((Player) sender,ConfigManager.Message.prefix + ConfigManager.Message.not_online.replace("{Player}",args[1])); + }else { + AdventureManager.consoleMessage(ConfigManager.Message.prefix + ConfigManager.Message.not_online.replace("{Player}",args[1])); + } + } + }else { + AdventureManager.playerMessage((Player) sender,ConfigManager.Message.prefix + ConfigManager.Message.noPerm); + } + return true; + } + case "unequip" -> { + if (sender instanceof Player player){ + DataManager.cache.get(player.getUniqueId()).equipNameplate("none"); + this.plugin.getScoreBoardManager().getTeam(player.getName()).updateNameplates(); + this.plugin.getDataManager().savePlayer(player.getUniqueId()); + AdventureManager.playerMessage(player, ConfigManager.Message.prefix + ConfigManager.Message.unequip); + }else { + AdventureManager.consoleMessage(ConfigManager.Message.prefix + ConfigManager.Message.no_console); + } + return true; + } + case "forceunequip" -> { + if (args.length < 2){ + if(sender instanceof Player){ + AdventureManager.playerMessage((Player) sender,ConfigManager.Message.prefix + ConfigManager.Message.lackArgs); + }else { + AdventureManager.consoleMessage(ConfigManager.Message.prefix + ConfigManager.Message.lackArgs); + } + return true; + } + if (sender.hasPermission("customnameplates.forceunequip")){ + if (Bukkit.getPlayer(args[1]) != null){ + Player player = Bukkit.getPlayer(args[1]); + DataManager.cache.get(player.getUniqueId()).equipNameplate("none"); + this.plugin.getScoreBoardManager().getTeam(args[1]).updateNameplates(); + this.plugin.getDataManager().savePlayer(player.getUniqueId()); + if (sender instanceof Player){ + AdventureManager.playerMessage((Player) sender, ConfigManager.Message.prefix + ConfigManager.Message.force_unequip.replace("{Player}", args[1])); + }else { + AdventureManager.consoleMessage(ConfigManager.Message.prefix + ConfigManager.Message.force_unequip.replace("{Player}", args[1])); + } + }else { + //玩家不存在,不在线 + if(sender instanceof Player){ + AdventureManager.playerMessage((Player) sender,ConfigManager.Message.prefix + ConfigManager.Message.not_online.replace("{Player}",args[1])); + }else { + AdventureManager.consoleMessage(ConfigManager.Message.prefix + ConfigManager.Message.not_online.replace("{Player}",args[1])); + } + } + } + } + case "preview" -> { + if (sender instanceof Player player){ + //指令冷却 + long time = System.currentTimeMillis(); + //冷却时间判断 + if (time - (coolDown.getOrDefault(player, time - ConfigManager.MainConfig.preview * 1050)) < ConfigManager.MainConfig.preview * 1050) { + AdventureManager.playerMessage(player, ConfigManager.Message.prefix + ConfigManager.Message.cooldown); + return true; + } + //重置冷却时间 + coolDown.put(player, time); + if (player.hasPermission("customnameplates.preview") || player.isOp()){ + AdventureManager.playerMessage(player,ConfigManager.Message.prefix + ConfigManager.Message.preview); + ArmorStand entity = player.getWorld().spawn(player.getLocation().add(0,0.8,0), ArmorStand.class, a -> { + a.setInvisible(true); + a.setCollidable(false); + a.setInvulnerable(true); + a.setVisible(false); + a.setCustomNameVisible(false); + a.setSmall(true); + a.setGravity(false); + }); + pCache.add(entity); + NameplatesTeam team = this.plugin.getScoreBoardManager().getOrCreateTeam(player); + Component full = team.getPrefix().append(Component.text(player.getName()).font(Key.key("default")).append(team.getSuffix())); + + WrappedDataWatcher wrappedDataWatcher = new WrappedDataWatcher(); + wrappedDataWatcher.setEntity(entity); + WrappedDataWatcher.Serializer serializer = WrappedDataWatcher.Registry.get(Boolean.class); + wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(2, WrappedDataWatcher.Registry.getChatComponentSerializer(true)), Optional.of(WrappedChatComponent.fromJson(GsonComponentSerializer.gson().serialize(full)).getHandle())); + wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(3, serializer), true); + PacketContainer packetContainer = new PacketContainer(PacketType.Play.Server.ENTITY_METADATA); + packetContainer.getIntegers().write(0, entity.getEntityId()); + packetContainer.getWatchableCollectionModifier().write(0, wrappedDataWatcher.getWatchableObjects()); + + try { + ProtocolLibrary.getProtocolManager().sendServerPacket(player, packetContainer); + } + catch (Exception e) { + AdventureManager.consoleMessage("[CustomNameplates] Error! Failed to preview for "+ player.getName()+""); + e.printStackTrace(); + } + BukkitScheduler bukkitScheduler = Bukkit.getScheduler(); + for (int i = 1; i < ConfigManager.MainConfig.preview * 20; i++){ + bukkitScheduler.runTaskLater(CustomNameplates.instance,()-> entity.teleport(player.getLocation().add(0,0.8,0)), i); + } + bukkitScheduler.runTaskLater(CustomNameplates.instance, ()->{ + entity.remove(); + pCache.remove(entity); + }, ConfigManager.MainConfig.preview * 20L); + }else { + AdventureManager.playerMessage((Player) sender,ConfigManager.Message.prefix + ConfigManager.Message.noPerm); + } + }else { + AdventureManager.consoleMessage(ConfigManager.Message.prefix + ConfigManager.Message.no_console); + } + } + case "list" -> { + if (sender instanceof Player player){ + if (player.isOp()){ + StringBuilder stringBuilder = new StringBuilder(); + this.plugin.getResourceManager().caches.keySet().forEach(key ->{ + if(key.equalsIgnoreCase("none")) return; + stringBuilder.append(key).append(" "); + }); + AdventureManager.playerMessage(player, ConfigManager.Message.prefix + ConfigManager.Message.available.replace("{Nameplates}", stringBuilder.toString())); + }else if(player.hasPermission("customnameplates.list")){ + StringBuilder stringBuilder = new StringBuilder(); + for (PermissionAttachmentInfo info : player.getEffectivePermissions()) { + String permission = info.getPermission().toLowerCase(); + if (permission.startsWith("customnameplates.equip.")) { + permission = StringUtils.replace(permission, "customnameplates.equip.", ""); + if (this.plugin.getResourceManager().caches.get(permission) != null){ + stringBuilder.append(permission).append(" "); + } + } + } + AdventureManager.playerMessage(player, ConfigManager.Message.prefix + ConfigManager.Message.available.replace("{Nameplates}", stringBuilder.toString())); + }else { + AdventureManager.playerMessage(player,ConfigManager.Message.prefix + ConfigManager.Message.noPerm); + } + }else { + AdventureManager.consoleMessage(ConfigManager.Message.prefix + ConfigManager.Message.no_console); + } + } + default -> { + if(sender instanceof Player player){ + if (player.hasPermission("customnameplates.help")){ + AdventureManager.playerMessage(player,"/nameplates help - show the command list"); + AdventureManager.playerMessage(player,"/nameplates reload - reload the configuration"); + AdventureManager.playerMessage(player,"/nameplates equip - equip a specified nameplate"); + AdventureManager.playerMessage(player,"/nameplates forceequip - force a player to equip a specified nameplate"); + AdventureManager.playerMessage(player,"/nameplates unequip - unequip your nameplate"); + AdventureManager.playerMessage(player,"/nameplates forceunequip - force unequip a player's nameplate"); + AdventureManager.playerMessage(player,"/nameplates preview - preview your nameplate"); + AdventureManager.playerMessage(player,"/nameplates list - list your available nameplates"); + } + }else { + AdventureManager.consoleMessage("/nameplates help - show the command list"); + AdventureManager.consoleMessage("/nameplates reload - reload the configuration"); + AdventureManager.consoleMessage("/nameplates equip - equip a specified nameplate"); + AdventureManager.consoleMessage("/nameplates forceequip - force a player to equip a specified nameplate"); + AdventureManager.consoleMessage("/nameplates unequip - unequip your nameplate"); + AdventureManager.consoleMessage("/nameplates forceunequip - force unequip a player's nameplate"); + AdventureManager.consoleMessage("/nameplates preview - preview your nameplate"); + AdventureManager.consoleMessage("/nameplates list - list your available nameplates"); + } + return true; + } + } + return true; + } +} diff --git a/src/main/java/net/momirealms/customnameplates/commands/TabComplete.java b/src/main/java/net/momirealms/customnameplates/commands/TabComplete.java new file mode 100644 index 0000000..6c891e4 --- /dev/null +++ b/src/main/java/net/momirealms/customnameplates/commands/TabComplete.java @@ -0,0 +1,86 @@ +package net.momirealms.customnameplates.commands; + +import net.momirealms.customnameplates.CustomNameplates; +import org.apache.commons.lang.StringUtils; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; +import org.bukkit.permissions.PermissionAttachmentInfo; +import org.jetbrains.annotations.Nullable; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.ArrayList; +import java.util.List; + +public class TabComplete implements TabCompleter { + + private final CustomNameplates plugin; + + public TabComplete(CustomNameplates plugin){ + this.plugin = plugin; + } + + @Override + @ParametersAreNonnullByDefault + public @Nullable List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + + if(1 == args.length){ + List tab = new ArrayList<>(); + if (sender.hasPermission("customnameplates.reload")) tab.add("reload"); + if (sender.hasPermission("customnameplates.help")) tab.add("help"); + if (sender.hasPermission("customnameplates.equip")) tab.add("equip"); + if (sender.hasPermission("customnameplates.forceequip")) tab.add("forceequip"); + if (sender.hasPermission("customnameplates.unequip")) tab.add("unequip"); + if (sender.hasPermission("customnameplates.forceunequip")) tab.add("forceunequip"); + if (sender.hasPermission("customnameplates.preview")) tab.add("preview"); + if (sender.hasPermission("customnameplates.list")) tab.add("list"); + return tab; + } + if(2 == args.length){ + List tab = new ArrayList<>(); + if (args[0].equalsIgnoreCase("equip")){ + return availableNameplates(sender); + } + if (args[0].equalsIgnoreCase("forceunequip") && sender.hasPermission("customnameplates.forceunequip")){ + return online_players(); + } + if (args[0].equalsIgnoreCase("forceequip") && sender.hasPermission("customnameplates.forceequip")){ + return online_players(); + } + } + if(3 == args.length){ + if (args[0].equalsIgnoreCase("forceequip") && sender.hasPermission("customnameplates.forceequip")){ + return nameplates(); + } + } + return null; + } + + private static List online_players(){ + List online = new ArrayList<>(); + Bukkit.getOnlinePlayers().forEach((player -> online.add(player.getName()))); + return online; + } + + private List availableNameplates(CommandSender sender){ + List availableNameplates = new ArrayList<>(); + if (sender instanceof Player player){ + for (PermissionAttachmentInfo info : player.getEffectivePermissions()) { + String permission = info.getPermission().toLowerCase(); + if (permission.startsWith("customnameplates.equip.")) { + permission = StringUtils.replace(permission, "customnameplates.equip.", ""); + if (this.plugin.getResourceManager().caches.get(permission) != null){ + availableNameplates.add(permission); + } + } + } + } + return availableNameplates; + } + + private List nameplates(){ + return new ArrayList<>(this.plugin.getResourceManager().caches.keySet()); + } +} diff --git a/src/main/java/net/momirealms/customnameplates/data/DataManager.java b/src/main/java/net/momirealms/customnameplates/data/DataManager.java new file mode 100644 index 0000000..61f255e --- /dev/null +++ b/src/main/java/net/momirealms/customnameplates/data/DataManager.java @@ -0,0 +1,58 @@ +package net.momirealms.customnameplates.data; + +import net.momirealms.customnameplates.AdventureManager; +import net.momirealms.customnameplates.ConfigManager; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class DataManager { + + public static Map cache; + public DataManager() { + cache = new HashMap<>(); + } + + public PlayerData getOrCreate(UUID uuid) { + if (cache.containsKey(uuid)) { + return cache.get(uuid); + } + PlayerData playerData = SqlHandler.getPlayerData(uuid); + if (playerData == null) { + playerData = PlayerData.EMPTY; + } + cache.put(uuid, playerData); + return playerData; + } + + public void unloadPlayer(UUID uuid) { + if (!cache.containsKey(uuid)) { + return; + } + SqlHandler.save(cache.get(uuid), uuid); + cache.remove(uuid); + } + + public void savePlayer(UUID uuid) { + SqlHandler.save(cache.get(uuid), uuid); + } + + public static boolean create() { + if(ConfigManager.DatabaseConfig.use_mysql){ + AdventureManager.consoleMessage("[CustomNameplates] Storage Mode - MYSQL"); + }else { + AdventureManager.consoleMessage("[CustomNameplates] Storage Mode - SQLite"); + } + if (SqlHandler.connect()) { + if (ConfigManager.DatabaseConfig.use_mysql) { + SqlHandler.getWaitTimeOut(); + } + SqlHandler.createTable(); + } else { + AdventureManager.consoleMessage("//DATA storage ERROR//"); + return false; + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/net/momirealms/customnameplates/data/PlayerData.java b/src/main/java/net/momirealms/customnameplates/data/PlayerData.java new file mode 100644 index 0000000..5e7adc4 --- /dev/null +++ b/src/main/java/net/momirealms/customnameplates/data/PlayerData.java @@ -0,0 +1,35 @@ +package net.momirealms.customnameplates.data; + +import net.momirealms.customnameplates.ConfigManager; + +public class PlayerData { + + public static PlayerData EMPTY; + static { + EMPTY = new PlayerData(ConfigManager.MainConfig.default_nameplate, 0); + } + + private String equipped; + private int accepted; + + public PlayerData(String equipped, int accepted) { + this.equipped = equipped; + this.accepted = accepted; + } + + public int getAccepted(){ + return this.accepted; + } + + public void setAccepted(int accepted){ + this.accepted = accepted; + } + + public String getEquippedNameplate() { + return this.equipped; + } + + public void equipNameplate(String nameplate) { + this.equipped = nameplate.toLowerCase(); + } +} diff --git a/src/main/java/net/momirealms/customnameplates/data/SqlHandler.java b/src/main/java/net/momirealms/customnameplates/data/SqlHandler.java new file mode 100644 index 0000000..4b96f21 --- /dev/null +++ b/src/main/java/net/momirealms/customnameplates/data/SqlHandler.java @@ -0,0 +1,132 @@ +package net.momirealms.customnameplates.data; + +import net.momirealms.customnameplates.AdventureManager; +import net.momirealms.customnameplates.ConfigManager; +import net.momirealms.customnameplates.utils.SqlConnection; +import org.bukkit.Bukkit; + +import java.sql.*; +import java.util.UUID; + +public class SqlHandler { + + public static String tableName = ConfigManager.DatabaseConfig.tableName; + public final static SqlConnection database = new SqlConnection(); + + public static boolean connect() { + return database.setGlobalConnection(); + } + + public static void close() { + database.close(); + } + + public static void getWaitTimeOut() { + if (ConfigManager.DatabaseConfig.use_mysql && !ConfigManager.DatabaseConfig.enable_pool) { + try { + Connection connection = database.getConnectionAndCheck(); + String query = "show variables LIKE 'wait_timeout'"; + PreparedStatement statement = connection.prepareStatement(query); + ResultSet rs = statement.executeQuery(); + if (rs.next()) { + int waitTime = rs.getInt(2); + if (waitTime > 50) { + database.waitTimeOut = waitTime - 30; + } + } + rs.close(); + statement.close(); + database.closeHikariConnection(connection); + } catch (SQLException ignored) { + AdventureManager.consoleMessage("[CustomNameplates] Failed to get wait time out"); + } + } + } + + public static void createTable() { + try { + Connection connection = database.getConnectionAndCheck(); + Statement statement = connection.createStatement(); + if (statement == null) { + return; + } + String query; + if (ConfigManager.DatabaseConfig.use_mysql) { + query = "CREATE TABLE IF NOT EXISTS " + tableName + + "(player VARCHAR(50) NOT NULL, equipped VARCHAR(50) NOT NULL, accepted INT(1) NOT NULL," + + " PRIMARY KEY (player)) DEFAULT charset = " + ConfigManager.DatabaseConfig.ENCODING + ";"; + } else { + query = "CREATE TABLE IF NOT EXISTS " + tableName + + "(player VARCHAR(50) NOT NULL, equipped VARCHAR(50) NOT NULL, accepted INT(1) NOT NULL," + + " PRIMARY KEY (player));"; + } + statement.executeUpdate(query); + statement.close(); + database.closeHikariConnection(connection); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public static PlayerData getPlayerData(UUID uuid) { + PlayerData playerData = null; + try { + Connection connection = database.getConnectionAndCheck(); + String sql = "SELECT * FROM " + tableName + " WHERE player = ?"; + PreparedStatement statement = connection.prepareStatement(sql); + statement.setString(1, uuid.toString()); + ResultSet rs = statement.executeQuery(); + if (rs.next()) { + playerData = new PlayerData(rs.getString(2), rs.getInt(3)); + }else { + sql = "INSERT INTO nameplates(player,equipped,accepted) values(?,?,?)"; + statement = connection.prepareStatement(sql); + statement.setString(1, uuid.toString()); + statement.setString(2, "none"); + statement.setInt(3, 0); + statement.executeUpdate(); + } + rs.close(); + statement.close(); + database.closeHikariConnection(connection); + } catch (SQLException e) { + e.printStackTrace(); + } + return playerData; + } + + public static void save(PlayerData playerData, UUID uuid) { + Connection connection = database.getConnectionAndCheck(); + try { + String query = " SET equipped = ?, accepted = ? WHERE player = ?"; + PreparedStatement statement = connection.prepareStatement("UPDATE " + tableName + query); + statement.setString(1, playerData.getEquippedNameplate()); + statement.setInt(2, playerData.getAccepted()); + statement.setString(3, uuid.toString()); + statement.executeUpdate(); + statement.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + database.closeHikariConnection(connection); + } + + public static void saveAll() { + Connection connection = database.getConnectionAndCheck(); + Bukkit.getOnlinePlayers().forEach(player -> { + try { + PlayerData playerData = DataManager.cache.get(player.getUniqueId()); + String query = " SET equipped = ?, accepted = ? WHERE player = ?"; + PreparedStatement statement = connection.prepareStatement("UPDATE " + tableName + query); + statement.setString(1, playerData.getEquippedNameplate()); + statement.setInt(2, playerData.getAccepted()); + statement.setString(3, String.valueOf(player.getUniqueId())); + statement.executeUpdate(); + statement.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + }); + database.closeHikariConnection(connection); + } +} diff --git a/src/main/java/net/momirealms/customnameplates/font/FontCache.java b/src/main/java/net/momirealms/customnameplates/font/FontCache.java new file mode 100644 index 0000000..31bd6b4 --- /dev/null +++ b/src/main/java/net/momirealms/customnameplates/font/FontCache.java @@ -0,0 +1,86 @@ +package net.momirealms.customnameplates.font; + +import net.momirealms.customnameplates.nameplates.NameplateConfig; + +public record FontCache(String name, FontChar fontChar, NameplateConfig config) { + + public static FontCache EMPTY; + static { + FontCache.EMPTY = new FontCache("none", new FontChar(' ', ' ', ' '), NameplateConfig.EMPTY); + } + + public String getName() { + return this.name; + } + + public FontChar getChar() { + return this.fontChar; + } + public NameplateConfig getConfig() { + return this.config; + } + + @Override + public boolean equals(final Object o) { + if (o == this) { + return true; + } + if (!(o instanceof FontCache fontCache)) { + return false; + } + if (!fontCache.canEqual(this)) { + return false; + } + final String name = this.getName(); + final String name2 = fontCache.getName(); + Label_a: + { + if (name == null) { + if (name2 == null) { + break Label_a; + } + } else if (name.equals(name2)) { + break Label_a; + } + return false; + } + FontChar info = this.getChar(); + FontChar info2 = fontCache.getChar(); + Label_b: + { + if (info == null) { + if (info2 == null) { + break Label_b; + } + } else if (info.equals(info2)) { + break Label_b; + } + return false; + } + final NameplateConfig config = this.getConfig(); + final NameplateConfig config2 = fontCache.getConfig(); + if (config == null) { + return config2 == null; + } else return config.equals(config2); + } + + @Override + public int hashCode() { + final int n = 1; + final String name = this.getName(); + final int n2 = n * 59 + ((name == null) ? 43 : name.hashCode()); + final FontChar fontChar = this.getChar(); + final int n3 = n2 * 59 + ((fontChar == null) ? 43 : fontChar.hashCode()); + final NameplateConfig config = this.getConfig(); + return n3 * 59 + ((config == null) ? 43 : config.hashCode()); + } + + @Override + public String toString() { + return "FontCache(name=" + this.getName() + ", info=" + this.getChar() + ", config=" + this.getConfig() + ")"; + } + + private boolean canEqual(final Object other) { + return other instanceof FontCache; + } +} diff --git a/src/main/java/net/momirealms/customnameplates/font/FontChar.java b/src/main/java/net/momirealms/customnameplates/font/FontChar.java new file mode 100644 index 0000000..584bfb6 --- /dev/null +++ b/src/main/java/net/momirealms/customnameplates/font/FontChar.java @@ -0,0 +1,43 @@ +package net.momirealms.customnameplates.font; + + +public record FontChar(char left, char middle, char right) { + + public char getLeft() { + return this.left; + } + + public char getMiddle() { + return this.middle; + } + + public char getRight() { + return this.right; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof FontChar fontInfo)) { + return false; + } + return this.getLeft() == fontInfo.getLeft() && this.getMiddle() == fontInfo.getMiddle() && this.getRight() == fontInfo.getRight(); + } + + /* + 如果对象的equals方法被重写,那么对象的HashCode方法也尽量重写 + 比如:有个A类重写了equals方法,但是没有重写hashCode方法,看输出结果,对象a1和对象a2使用equals方法相等, + 所以,我们在重写了equals方法后,尽量也重写了hashcode方法,通过一定的算法,使他们在equals相等时,也会有相同的hashcode值。 + */ + @Override + public int hashCode() { + return ((59 + this.getLeft()) * 59 + this.getMiddle()) * 59 + this.getRight(); + } + + @Override + public String toString() { + return "FontChar=" + this.getLeft() + this.getMiddle() + this.getRight(); + } +} \ No newline at end of file diff --git a/src/main/java/net/momirealms/customnameplates/font/FontNegative.java b/src/main/java/net/momirealms/customnameplates/font/FontNegative.java new file mode 100644 index 0000000..c7153d9 --- /dev/null +++ b/src/main/java/net/momirealms/customnameplates/font/FontNegative.java @@ -0,0 +1,97 @@ +package net.momirealms.customnameplates.font; + +public enum FontNegative { + + /* + 实际向左移的距离是height(尺寸)-2 + */ + NEG_1('\uf801', -1, -3), + NEG_2('\uf802', -2, -4), + NEG_3('\uf803', -3, -5), + NEG_4('\uf804', -4, -6), + NEG_5('\uf805', -5, -7), + NEG_6('\uf806', -6, -8), + NEG_7('\uf807', -7, -9), + NEG_8('\uf808', -8, -10), + NEG_16('\uf809', -16, -18), + NEG_32('\uf80a', -32, -34), + NEG_64('\uf80b', -64, -66), + NEG_128('\uf80c', -128, -130); + + private final char character; + private final int space; + private final int height; + + FontNegative(char character, int space, int height) { + this.character = character; + this.space = space; + this.height = height; + } + + /* + 获取最短的负空格字符 + */ + public static String getShortestNegChars(int n) { + StringBuilder stringBuilder = new StringBuilder(); + if (n > 128) { + stringBuilder.append(FontNegative.NEG_128.getCharacter()); + n -= 129; + } + if (n - 64 > 0) { + stringBuilder.append(FontNegative.NEG_64.getCharacter()); + n -= 64; + } + if (n - 32 > 0) { + stringBuilder.append(FontNegative.NEG_32.getCharacter()); + n -= 32; + } + if (n - 16 > 0) { + stringBuilder.append(FontNegative.NEG_16.getCharacter()); + n -= 16; + } + if (n - 8 > 0) { + stringBuilder.append(FontNegative.NEG_8.getCharacter()); + n -= 8; + } + if (n - 7 > 0) { + stringBuilder.append(FontNegative.NEG_7.getCharacter()); + n -= 7; + } + if (n - 6 > 0) { + stringBuilder.append(FontNegative.NEG_6.getCharacter()); + n -= 6; + } + if (n - 5 > 0) { + stringBuilder.append(FontNegative.NEG_5.getCharacter()); + n -= 5; + } + if (n - 4 > 0) { + stringBuilder.append(FontNegative.NEG_4.getCharacter()); + n -= 4; + } + if (n - 3 > 0) { + stringBuilder.append(FontNegative.NEG_3.getCharacter()); + n -= 3; + } + if (n - 2 > 0) { + stringBuilder.append(FontNegative.NEG_2.getCharacter()); + n -= 2; + } + if (n - 1 > 0) { + stringBuilder.append(FontNegative.NEG_1.getCharacter()); + } + return stringBuilder.toString(); + } + + public char getCharacter() { + return this.character; + } + + public int getSpace() { + return this.space; + } + + public int getHeight() { + return this.height; + } +} diff --git a/src/main/java/net/momirealms/customnameplates/font/FontWidth.java b/src/main/java/net/momirealms/customnameplates/font/FontWidth.java new file mode 100644 index 0000000..e8596cc --- /dev/null +++ b/src/main/java/net/momirealms/customnameplates/font/FontWidth.java @@ -0,0 +1,78 @@ +package net.momirealms.customnameplates.font; + +public enum FontWidth { + + A('A', 5), a('a', 5), B('B', 5), b('b', 5), + C('C', 5), c('c', 5), D('D', 5), d('d', 5), + E('E', 5), e('e', 5), F('F', 5), f('f', 4), + G('G', 5), g('g', 5), H('H', 5), h('h', 5), + I('I', 3), i('i', 1), J('J', 5), j('j', 5), + K('K', 5), k('k', 4), L('L', 5), l('l', 2), + M('M', 5), m('m', 5), N('N', 5), n('n', 5), + O('O', 5), o('o', 5), P('P', 5), p('p', 5), + Q('Q', 5), q('q', 5), R('R', 5), r('r', 5), + S('S', 5), s('s', 5), T('T', 5), t('t', 4), + U('U', 5), u('u', 5), V('V', 5), v('v', 5), + W('W', 5), w('w', 5), X('X', 5), x('x', 5), + Y('Y', 5), y('y', 5), Z('Z', 5), z('z', 5), + NUM_1('1', 5), NUM_2('2', 5), NUM_3('3', 5), NUM_4('4', 5), + NUM_5('5', 5), NUM_6('6', 5), NUM_7('7', 5), NUM_8('8', 5), + NUM_9('9', 5), NUM_0('0', 5), EXCLAMATION_POINT('!', 1), AT_SYMBOL('@', 6), + NUM_SIGN('#', 5), DOLLAR_SIGN('$', 5), PERCENT('%', 5), UP_ARROW('^', 5), + AMPERSAND('&', 5), ASTERISK('*', 5), LEFT_PARENTHESIS('(', 4), + RIGHT_PARENTHESIS(')', 4), MINUS('-', 5), UNDERSCORE('_', 5), PLUS_SIGN('+', 5), + EQUALS_SIGN('=', 5), LEFT_CURL_BRACE('{', 4), RIGHT_CURL_BRACE('}', 4), + LEFT_BRACKET('[', 3), RIGHT_BRACKET(']', 3), COLON(':', 1), SEMI_COLON(';', 1), + DOUBLE_QUOTE('\"', 3), SINGLE_QUOTE('\'', 1), LEFT_ARROW('<', 4), + RIGHT_ARROW('>', 4), QUESTION_MARK('?', 5), SLASH('/', 5), + BACK_SLASH('\\', 5), LINE('|', 1), TILDE('~', 5), TICK('`', 2), + PERIOD('.', 1), COMMA(',', 1), SPACE(' ', 3), + IN_BETWEEN(' ', 1), DEFAULT('默', 8), DEFAULT2('米', 7); + + private final char character; + private final int length; + + FontWidth(char character, int length) { + this.character = character; + this.length = length; + } + + public char getCharacter() { + return this.character; + } + + public int getLength() { + return this.length; + } + + public int getBoldLength() { + if (this == FontWidth.SPACE) { + return this.getLength(); + } + return this.getLength() + 1; + } + + /* + 获取每个字符的像素宽度 + */ + public static FontWidth getInfo(char c) { + for (FontWidth minecraftFontWidth : values()) { + if (minecraftFontWidth.getCharacter() == c) { + return minecraftFontWidth; + } + } + return FontWidth.DEFAULT; + } + + /* + 计算一个字符串的总宽度 + */ + public static int getTotalWidth(String s) { + int length = s.length(); + int n = 0; + for (int i = 0; i < length; i++) { + n += getInfo(s.charAt(i)).getLength(); + } + return n + FontWidth.IN_BETWEEN.getLength() * (length - 1); //总长还需加上字符间距 + } +} diff --git a/src/main/java/net/momirealms/customnameplates/listener/PacketsListener.java b/src/main/java/net/momirealms/customnameplates/listener/PacketsListener.java new file mode 100644 index 0000000..8414f4e --- /dev/null +++ b/src/main/java/net/momirealms/customnameplates/listener/PacketsListener.java @@ -0,0 +1,55 @@ +package net.momirealms.customnameplates.listener; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.events.InternalStructure; +import com.comphenix.protocol.events.ListenerPriority; +import com.comphenix.protocol.events.PacketAdapter; +import com.comphenix.protocol.events.PacketEvent; +import com.comphenix.protocol.utility.MinecraftReflection; +import com.comphenix.protocol.wrappers.WrappedChatComponent; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.momirealms.customnameplates.ConfigManager; +import net.momirealms.customnameplates.CustomNameplates; +import net.momirealms.customnameplates.data.DataManager; +import net.momirealms.customnameplates.scoreboard.NameplatesTeam; +import org.bukkit.ChatColor; + +import java.util.Optional; + +public class PacketsListener extends PacketAdapter { + + private final CustomNameplates plugin; + + public PacketsListener(CustomNameplates plugin) { + super(plugin, ListenerPriority.HIGHEST, PacketType.Play.Server.SCOREBOARD_TEAM); + this.plugin = plugin; + } + + public void onPacketSending(PacketEvent event) { + Integer n = event.getPacket().getIntegers().read(0); + //create team && update team info + if (n != 0 && n != 2) { + return; + } + Optional optional = event.getPacket().getOptionalStructures().read(0); + if (optional.isEmpty()) { + return; + } + InternalStructure internalStructure = optional.get(); + String teamName = event.getPacket().getStrings().read(0); + NameplatesTeam team = this.plugin.getScoreBoardManager().getTeam(teamName); + if (!this.plugin.getScoreBoardManager().doesTeamExist(teamName)){ + return; + } + if (ConfigManager.MainConfig.show_after && DataManager.cache.get(event.getPlayer().getUniqueId()).getAccepted() == 0) { + internalStructure.getChatComponents().write(1, WrappedChatComponent.fromJson("{\"text\":\"\"}")); + internalStructure.getChatComponents().write(2, WrappedChatComponent.fromJson("{\"text\":\"\"}")); + internalStructure.getEnumModifier(ChatColor.class, MinecraftReflection.getMinecraftClass("EnumChatFormat")).write(0,ChatColor.WHITE); + return; + } + //在新建队伍名字的时候其实就是以玩家名命名,所以获得的teamName=playerName + internalStructure.getChatComponents().write(1, WrappedChatComponent.fromJson(GsonComponentSerializer.gson().serialize(team.getPrefix()))); + internalStructure.getChatComponents().write(2, WrappedChatComponent.fromJson(GsonComponentSerializer.gson().serialize(team.getSuffix()))); + internalStructure.getEnumModifier(ChatColor.class, MinecraftReflection.getMinecraftClass("EnumChatFormat")).write(0,team.getColor()); + } +} diff --git a/src/main/java/net/momirealms/customnameplates/listener/PlayerListener.java b/src/main/java/net/momirealms/customnameplates/listener/PlayerListener.java new file mode 100644 index 0000000..d833ebb --- /dev/null +++ b/src/main/java/net/momirealms/customnameplates/listener/PlayerListener.java @@ -0,0 +1,40 @@ +package net.momirealms.customnameplates.listener; + +import net.momirealms.customnameplates.CustomNameplates; +import net.momirealms.customnameplates.data.DataManager; +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.AsyncPlayerPreLoginEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.PlayerResourcePackStatusEvent; + +public record PlayerListener(CustomNameplates plugin) implements Listener { + + @EventHandler + public void onPreLogin(AsyncPlayerPreLoginEvent event) { + this.plugin.getDataManager().getOrCreate(event.getUniqueId()); + } + + @EventHandler + public void onJoin(PlayerJoinEvent event) { + this.plugin.getScoreBoardManager().getOrCreateTeam(event.getPlayer()); + } + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + this.plugin.getDataManager().unloadPlayer(event.getPlayer().getUniqueId()); + } + + @EventHandler + public void onAccept(PlayerResourcePackStatusEvent event) { + if (event.getStatus() == PlayerResourcePackStatusEvent.Status.SUCCESSFULLY_LOADED) { + DataManager.cache.get(event.getPlayer().getUniqueId()).setAccepted(1); + Bukkit.getOnlinePlayers().forEach(player -> this.plugin.getScoreBoardManager().getTeam(player.getName()).updateNameplates()); + } else { + DataManager.cache.get(event.getPlayer().getUniqueId()).setAccepted(0); + Bukkit.getOnlinePlayers().forEach(player -> this.plugin.getScoreBoardManager().getTeam(player.getName()).updateNameplates()); + } + } +} diff --git a/src/main/java/net/momirealms/customnameplates/nameplates/NameplateConfig.java b/src/main/java/net/momirealms/customnameplates/nameplates/NameplateConfig.java new file mode 100644 index 0000000..84ba292 --- /dev/null +++ b/src/main/java/net/momirealms/customnameplates/nameplates/NameplateConfig.java @@ -0,0 +1,27 @@ +package net.momirealms.customnameplates.nameplates; + +import org.bukkit.ChatColor; + +public record NameplateConfig(ChatColor color, int height, String name) { + + public static NameplateConfig EMPTY; + + static { + EMPTY = new NameplateConfig(ChatColor.WHITE, 16, "none"); + } + + //获取Team颜色 + public ChatColor getColor() { + return this.color; + } + + //获取自定义font大小 + public int getHeight() { + return this.height; + } + + //获取铭牌名 + public String getName() { + return this.name; + } +} diff --git a/src/main/java/net/momirealms/customnameplates/nameplates/NameplateUtil.java b/src/main/java/net/momirealms/customnameplates/nameplates/NameplateUtil.java new file mode 100644 index 0000000..29d9d0a --- /dev/null +++ b/src/main/java/net/momirealms/customnameplates/nameplates/NameplateUtil.java @@ -0,0 +1,57 @@ +package net.momirealms.customnameplates.nameplates; + +import net.momirealms.customnameplates.font.FontCache; +import net.momirealms.customnameplates.font.FontNegative; +import net.momirealms.customnameplates.font.FontWidth; +import org.bukkit.ChatColor; + +public class NameplateUtil { + + private final FontCache fontcache; + + public NameplateUtil(FontCache font) { + this.fontcache = font; + } + + /* + 根据玩家名构造长度适合的铭牌字符 + 当然这个玩家名是带上前缀与后缀的 + */ + public String makeCustomNameplate(String prefix, String name, String suffix) { + int totalWidth = FontWidth.getTotalWidth(ChatColor.stripColor(prefix + name + suffix)); + boolean isEven = totalWidth % 2 == 0; //奇偶判断 + char left = this.fontcache.getChar().getLeft(); + char middle = this.fontcache.getChar().getMiddle(); + char right = this.fontcache.getChar().getRight(); + char neg_1 = FontNegative.NEG_1.getCharacter(); + int left_offset = totalWidth + 16 + 1; + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(FontNegative.getShortestNegChars(isEven ? left_offset : left_offset + 1)); //先向左平移一个正方形的距离 + stringBuilder.append(left).append(neg_1); //将铭牌的左部分拼接 + int mid_amount = (totalWidth + 1) / 16; //显示名称的总长,如果超过一个正方形则多复制几个正方形 + for (int i = 0; i < (mid_amount == 0 ? 1 : mid_amount); i++) { + stringBuilder.append(middle).append(neg_1); //减一是字符之间的间距(3) + } + stringBuilder.append(FontNegative.getShortestNegChars(16 - ((totalWidth + 1) % 16 + (isEven ? 0 : 1)))); + stringBuilder.append(middle).append(neg_1); + stringBuilder.append(right).append(neg_1); //将铭牌的右部分拼接 + stringBuilder.append(FontNegative.getShortestNegChars(isEven ? left_offset : left_offset + 1)); //首尾对称处理,保证铭牌位于正中央 + return stringBuilder.toString(); + } + + /* + 用于为增加了后缀的玩家名计算负空格 + 保证铭牌总是位于玩家头顶中央的位置 + */ + public String getSuffixLength(String name) { + final int totalWidth = FontWidth.getTotalWidth(ChatColor.stripColor(name)); + return FontNegative.getShortestNegChars(totalWidth + totalWidth % 2 + 1); + } + + /* + 获取铭牌上玩家名的颜色 + */ + public ChatColor getColor() { + return this.fontcache.getConfig().getColor(); + } +} diff --git a/src/main/java/net/momirealms/customnameplates/resource/ResourceManager.java b/src/main/java/net/momirealms/customnameplates/resource/ResourceManager.java new file mode 100644 index 0000000..8d4d454 --- /dev/null +++ b/src/main/java/net/momirealms/customnameplates/resource/ResourceManager.java @@ -0,0 +1,237 @@ +package net.momirealms.customnameplates.resource; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import net.momirealms.customnameplates.ConfigManager; +import net.momirealms.customnameplates.CustomNameplates; +import net.momirealms.customnameplates.AdventureManager; +import net.momirealms.customnameplates.font.FontCache; +import net.momirealms.customnameplates.font.FontChar; +import net.momirealms.customnameplates.font.FontNegative; +import net.momirealms.customnameplates.nameplates.NameplateConfig; +import org.apache.commons.io.FileUtils; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.*; + +public class ResourceManager { + + public final HashMap caches; + private CustomNameplates plugin; + + public ResourceManager(CustomNameplates plugin) { + this.caches = new HashMap<>(); + this.plugin = plugin; + } + + /* + 此方法用于生成资源包 + */ + public void generateResourcePack() { + + File r_file = new File(CustomNameplates.instance.getDataFolder() + File.separator + "resources"); + File g_file = new File(CustomNameplates.instance.getDataFolder() + File.separator + "generated"); + //如果资源文件夹不存在则创建 + if (!r_file.exists()) { + AdventureManager.consoleMessage("[CustomNameplates] Failed to detect resources folder! Generating default resources..."); + if (!r_file.mkdir()) { + AdventureManager.consoleMessage("[CustomNameplates] Error! Failed to create resources folder..."); + return; + } + saveDefaultResources(); + } + //获取资源文件夹下的所有png文件 + File[] pngFiles = r_file.listFiles(file -> file.getName().endsWith(".png")); + if (pngFiles == null) { + AdventureManager.consoleMessage("[CustomNameplates] Error! No png files detected in resource folder..."); + return; + } + Arrays.sort(pngFiles); //将png文件按照首字母进行排序 + deleteDirectory(g_file); //删除文件夹以重置自动生成的资源 + + File f_file = new File(CustomNameplates.instance.getDataFolder() + File.separator + "generated" + File.separatorChar + ConfigManager.MainConfig.namespace + File.separatorChar + "font"); + File t_file = new File(CustomNameplates.instance.getDataFolder() + File.separator + "generated" + File.separatorChar + ConfigManager.MainConfig.namespace + File.separatorChar + "textures"); + + if (!f_file.mkdirs() || !t_file.mkdirs()) { + AdventureManager.consoleMessage("[CustomNameplates] Error! Failed to generate resource pack folders..."); + return; + } + char start = ConfigManager.MainConfig.start_char.charAt(0); //获取起始字符 + JsonObject jsonObject_1 = new JsonObject(); //新建json对象 + JsonArray jsonArray_1 = new JsonArray(); + jsonObject_1.add("providers", jsonArray_1); + for (File png : pngFiles) { + JsonObject jsonObject_2 = new JsonObject(); + char left = start; + char middle; + char right; + start = (char)((right = (char)((middle = (char)(start + '\u0001')) + '\u0001')) + '\u0001'); //依次+1 + FontChar fontChar = new FontChar(left, middle, right); + String pngName = png.getName().substring(0, png.getName().length() - 4); //删除.png后缀 + NameplateConfig config = this.getConfiguration(pngName); + caches.put(pngName, new FontCache(pngName, fontChar, config)); + jsonObject_2.add("type", new JsonPrimitive("bitmap")); + jsonObject_2.add("file", new JsonPrimitive(ConfigManager.MainConfig.namespace + ":" + ConfigManager.MainConfig.folder_path.replaceAll("\\\\","/") + png.getName().toLowerCase())); + jsonObject_2.add("ascent", new JsonPrimitive(12)); + jsonObject_2.add("height", new JsonPrimitive(config.getHeight())); + JsonArray jsonArray_2 = new JsonArray(); + jsonArray_2.add(native2ascii(fontChar.getLeft()) + native2ascii(fontChar.getMiddle()) + native2ascii(fontChar.getRight())); + jsonObject_2.add("chars", jsonArray_2); + jsonArray_1.add(jsonObject_2); + try{ + FileUtils.copyFile(png, new File(t_file.getPath() + File.separatorChar + ConfigManager.MainConfig.folder_path + png.getName())); + }catch (IOException e){ + e.printStackTrace(); + AdventureManager.consoleMessage("[CustomNameplates] Error! Failed to copy png files to resource pack..."); + } + } + caches.put("none", FontCache.EMPTY); + CustomNameplates.instance.saveResource("space_split.png", false); //复制space_split.png + try{ + FileUtils.copyFile(new File(CustomNameplates.instance.getDataFolder(),"space_split.png"), new File(t_file.getPath() + File.separatorChar + ConfigManager.MainConfig.folder_path + "space_split.png")); + }catch (IOException e){ + e.printStackTrace(); + AdventureManager.consoleMessage("[CustomNameplates] Error! Failed to copy space_split.png to resource pack..."); + return; + } + new File(CustomNameplates.instance.getDataFolder(),"space_split.png").delete(); //删除拷贝出的默认文件 + this.getNegativeFontEnums().forEach(jsonArray_1::add); //添加负空格 + //存储default.json + try (FileWriter fileWriter = new FileWriter(f_file.getPath() + File.separatorChar + ConfigManager.MainConfig.font + ".json")) { + fileWriter.write(jsonObject_1.toString().replace("\\\\", "\\")); + } catch (IOException e) { + e.printStackTrace(); + AdventureManager.consoleMessage("[CustomNameplates] Error! Failed to generate font json..."); + return; + } + //资源包生成成功提示 + AdventureManager.consoleMessage("[CustomNameplates] Resource pack has been successfully generated! " + this.caches.size() + " nameplates Loaded."); + if (this.plugin.getHookManager().hasItemsAdder()){ + try{ + FileUtils.copyDirectory(g_file, new File(Bukkit.getPluginManager().getPlugin("ItemsAdder").getDataFolder() + File.separator + "data"+ File.separator + "resource_pack" + File.separator + "assets") ); + }catch (IOException e){ + e.printStackTrace(); + AdventureManager.consoleMessage("[CustomNameplates] Error! Failed to copy files to ItemsAdder..."); + } + } + } + + /* + 保存插件预设资源 + */ + private void saveDefaultResources() { + List list = Arrays.asList("cat", "egg", "cheems", "wither"); + list.forEach(name -> CustomNameplates.instance.saveResource("resources" + File.separatorChar + name + ".png", false)); + } + + /* + 删除文件夹 + */ + private void deleteDirectory(File file){ + if(file.exists()){ + try{ + FileUtils.deleteDirectory(file); + AdventureManager.consoleMessage("[CustomNameplates] Successfully copy files to ItemsAdder..."); + }catch (IOException e){ + e.printStackTrace(); + AdventureManager.consoleMessage("[CustomNameplates] Error! Failed to delete generated folder..." ); + } + } + } + + /* + 获取铭牌的config + */ + private NameplateConfig getConfiguration(String nameplate) { + try { + File file = new File(CustomNameplates.instance.getDataFolder().getPath() + File.separator + "resources" + File.separator + nameplate + ".yml"); + if (!file.exists()){ + try { + file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + AdventureManager.consoleMessage("铭牌配置生成出错!"); + } + } + YamlConfiguration config = new YamlConfiguration(); + config.load(CustomNameplates.instance.getDataFolder().getPath() + File.separator + "resources" + File.separator + nameplate + ".yml"); + if (!config.contains("name")){ + config.set("name", nameplate); + } + if (!config.contains("color")){ + config.set("color","WHITE"); + } + if (!config.contains("size")){ + config.set("size", 12); + } + ChatColor color = ChatColor.WHITE; + try { + color = ChatColor.valueOf(Objects.requireNonNull(config.getString("color")).toUpperCase()); + } + catch (IllegalArgumentException ex) { + AdventureManager.consoleMessage("[CustomNameplates] Invalid Color of " + nameplate + ""); + } + int size = config.getInt("size"); + String name = config.getString("name"); + config.save(file); + return new NameplateConfig(color, size, name); + } + catch (Exception e) { + return NameplateConfig.EMPTY; + } + } + + /* + 获取负空格并返回list + */ + private List getNegativeFontEnums() { + ArrayList list = new ArrayList<>(); + for (FontNegative negativeFont : FontNegative.values()) { + list.add(this.getNegativeFontChar(negativeFont.getHeight(), negativeFont.getCharacter())); + } + return list; + } + private JsonObject getNegativeFontChar(int height, char character) { + JsonObject jsonObject = new JsonObject(); + jsonObject.add("type", new JsonPrimitive("bitmap")); + jsonObject.add("file", new JsonPrimitive(ConfigManager.MainConfig.namespace + ":" + ConfigManager.MainConfig.folder_path.replaceAll("\\\\","/") +"space_split.png")); + jsonObject.add("ascent", new JsonPrimitive(-5000)); + jsonObject.add("height", new JsonPrimitive(height)); + final JsonArray jsonArray = new JsonArray(); + jsonArray.add(native2ascii(character)); + jsonObject.add("chars", jsonArray); + return jsonObject; + } + + /* + 根据铭牌名获取铭牌的FontCache + */ + public FontCache getNameplateInfo(String nameplate) { + return caches.get(nameplate); + } + + /* + 字符转换 + */ + private String native2ascii(char ch) { + if (ch > '\u007f') { + StringBuilder stringBuilder_1 = new StringBuilder("\\u"); + StringBuilder stringBuilder_2 = new StringBuilder(Integer.toHexString(ch)); + stringBuilder_2.reverse(); + for (int n = 4 - stringBuilder_2.length(), i = 0; i < n; i++) { + stringBuilder_2.append('0'); + } + for (int j = 0; j < 4; j++) { + stringBuilder_1.append(stringBuilder_2.charAt(3 - j)); + } + return stringBuilder_1.toString(); + } + return Character.toString(ch); + } +} \ No newline at end of file diff --git a/src/main/java/net/momirealms/customnameplates/scoreboard/NameplatesTeam.java b/src/main/java/net/momirealms/customnameplates/scoreboard/NameplatesTeam.java new file mode 100644 index 0000000..3d008d7 --- /dev/null +++ b/src/main/java/net/momirealms/customnameplates/scoreboard/NameplatesTeam.java @@ -0,0 +1,89 @@ +package net.momirealms.customnameplates.scoreboard; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import net.momirealms.customnameplates.ConfigManager; +import net.momirealms.customnameplates.CustomNameplates; +import net.momirealms.customnameplates.data.DataManager; +import net.momirealms.customnameplates.font.FontCache; +import net.momirealms.customnameplates.nameplates.NameplateUtil; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.scoreboard.Scoreboard; +import org.bukkit.scoreboard.Team; + +import java.util.Optional; + +public class NameplatesTeam { + + private final CustomNameplates plugin; + private final Player player; + private final Team team; + private Component prefix; + private Component suffix; + private ChatColor color; + + public void hideNameplate() { + this.team.setOption(Team.Option.NAME_TAG_VISIBILITY, Team.OptionStatus.NEVER); + } + public void showNameplate() { + this.team.setOption(Team.Option.NAME_TAG_VISIBILITY, Team.OptionStatus.ALWAYS); + } + public Component getPrefix() { + return this.prefix; + } + public Component getSuffix() { + return this.suffix; + } + public ChatColor getColor() { + return this.color; + } + + public NameplatesTeam(CustomNameplates plugin, Player player) { + this.color = ChatColor.WHITE; + this.plugin = plugin; + this.player = player; + Scoreboard scoreboard = Bukkit.getScoreboardManager().getMainScoreboard(); + String name = player.getName(); + this.team = Optional.ofNullable(Bukkit.getScoreboardManager().getMainScoreboard().getTeam(name)).orElseGet(() -> scoreboard.registerNewTeam(name)); + team.addEntry(player.getName()); + } + + public void updateNameplates() { + //获取玩家的铭牌,没有数据则创建数据,没有铭牌则设置为空铭牌 + String nameplate = (this.plugin.getDataManager().getOrCreate(this.player.getUniqueId())).getEquippedNameplate(); + //如果是空铭牌直接飞机票送走 + if (nameplate.equals("none")) { + this.prefix = Component.text(""); + this.suffix = Component.text(""); + this.color = ChatColor.WHITE; + this.team.setPrefix(""); + return; + } + //根据铭牌名获取FontCache + FontCache fontCache = this.plugin.getResourceManager().getNameplateInfo(nameplate); + if (fontCache == null){ + DataManager.cache.get(player.getUniqueId()).equipNameplate("none"); + return; + } + NameplateUtil nameplateUtil = new NameplateUtil(fontCache); + String name = this.player.getName(); + String playerPrefix; + String playerSuffix; + //有Papi才解析 + if (plugin.getHookManager().hasPlaceholderAPI()) { + playerPrefix = this.plugin.getHookManager().parsePlaceholders(this.player, ConfigManager.MainConfig.player_prefix); + playerSuffix = this.plugin.getHookManager().parsePlaceholders(this.player, ConfigManager.MainConfig.player_suffix); + }else { + playerPrefix = ConfigManager.MainConfig.player_prefix; + playerSuffix = ConfigManager.MainConfig.player_suffix; + } + //最终prefix: 偏移 + 铭牌左 + 偏移 + 铭牌中 + 偏移 + 铭牌右 + 偏移 + 前缀 + //最终suffix: 偏移 + 后缀 + this.prefix = Component.text(nameplateUtil.makeCustomNameplate(playerPrefix, name, playerSuffix)).font(ConfigManager.MainConfig.key).append(Component.text(playerPrefix).font(Key.key("default"))); + this.suffix = Component.text(playerSuffix).append(Component.text(nameplateUtil.getSuffixLength(playerPrefix + name + playerSuffix)).font(ConfigManager.MainConfig.key)); + this.color = nameplateUtil.getColor(); + this.team.setPrefix(""); + } +} \ No newline at end of file diff --git a/src/main/java/net/momirealms/customnameplates/scoreboard/ScoreBoardManager.java b/src/main/java/net/momirealms/customnameplates/scoreboard/ScoreBoardManager.java new file mode 100644 index 0000000..6c87e7e --- /dev/null +++ b/src/main/java/net/momirealms/customnameplates/scoreboard/ScoreBoardManager.java @@ -0,0 +1,46 @@ +package net.momirealms.customnameplates.scoreboard; + +import net.momirealms.customnameplates.CustomNameplates; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; + +public class ScoreBoardManager { + + private final CustomNameplates plugin; + /* + 虽然会内存占用会随着玩家数量持续增加,但是这点内存根本不值一提 + */ + private final Map teams; + + /* + 该类存在的意义是判断玩家是否已经 + */ + public ScoreBoardManager(CustomNameplates plugin) { + this.teams = new HashMap<>(); + this.plugin = plugin; + } + + public NameplatesTeam getOrCreateTeam(Player player) { + if (!this.teams.containsKey(player.getName())) { + this.teams.put(player.getName(), new NameplatesTeam(this.plugin, player)); + } + //延后一秒确保数据库完成请求 + Bukkit.getScheduler().runTaskLater(this.plugin, () -> this.getTeam(player.getName()).updateNameplates(), 20); + return this.teams.get(player.getName()); + } + + public void removeTeam(String playerName) { + this.teams.remove(playerName); + } + + public NameplatesTeam getTeam(String team) { + return this.teams.get(team); + } + + public boolean doesTeamExist(String playerName) { + return this.teams.containsKey(playerName); + } +} diff --git a/src/main/java/net/momirealms/customnameplates/utils/SqlConnection.java b/src/main/java/net/momirealms/customnameplates/utils/SqlConnection.java new file mode 100644 index 0000000..c409104 --- /dev/null +++ b/src/main/java/net/momirealms/customnameplates/utils/SqlConnection.java @@ -0,0 +1,184 @@ +package net.momirealms.customnameplates.utils; + +import com.zaxxer.hikari.HikariDataSource; +import net.momirealms.customnameplates.AdventureManager; +import net.momirealms.customnameplates.ConfigManager; +import net.momirealms.customnameplates.CustomNameplates; + +import java.io.File; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +public class SqlConnection { + + private String driver = "com.mysql.jdbc.Driver"; + + private final File dataFolder = CustomNameplates.instance.getDataFolder(); + + private boolean secon = false; + private boolean isfirstry = true; + + public int waitTimeOut = 10; + + public File userdata = new File(dataFolder, "data.db"); + private Connection connection = null; + private HikariDataSource hikari = null; + + /* + 新建Hikari配置 + */ + private void createNewHikariConfiguration() { + hikari = new HikariDataSource(); + hikari.setPoolName("[Nameplates]"); + hikari.setJdbcUrl(ConfigManager.DatabaseConfig.url); + hikari.setUsername(ConfigManager.DatabaseConfig.user); + hikari.setPassword(ConfigManager.DatabaseConfig.password); + hikari.setMaximumPoolSize(ConfigManager.DatabaseConfig.maximum_pool_size); + hikari.setMinimumIdle(ConfigManager.DatabaseConfig.minimum_idle); + hikari.setMaxLifetime(ConfigManager.DatabaseConfig.maximum_lifetime); + hikari.addDataSourceProperty("cachePrepStmts", "true"); + hikari.addDataSourceProperty("prepStmtCacheSize", "250"); + hikari.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); + hikari.addDataSourceProperty("userServerPrepStmts", "true"); + if (hikari.getMinimumIdle() < hikari.getMaximumPoolSize()) { + hikari.setIdleTimeout(ConfigManager.DatabaseConfig.idle_timeout); + } else { + hikari.setIdleTimeout(0); + } + } + + /* + 设置驱动,区分Mysql5与8 + */ + private void setDriver() { + if(ConfigManager.DatabaseConfig.use_mysql){ + try { + Class.forName("com.mysql.cj.jdbc.Driver"); + } catch (ClassNotFoundException e) { + driver = ("com.mysql.jdbc.Driver"); + return; + } + driver = ("com.mysql.cj.jdbc.Driver"); + }else { + driver = ("org.sqlite.JDBC"); + } + } + + public boolean setGlobalConnection() { + setDriver(); + try { + if (ConfigManager.DatabaseConfig.enable_pool) { + createNewHikariConfiguration(); + Connection connection = getConnection(); + closeHikariConnection(connection); + } else { + Class.forName(driver); + if(ConfigManager.DatabaseConfig.use_mysql){ + connection = DriverManager.getConnection(ConfigManager.DatabaseConfig.url, ConfigManager.DatabaseConfig.user, ConfigManager.DatabaseConfig.password); + }else { + connection = DriverManager.getConnection("jdbc:sqlite:" + userdata.toString()); + } + } + if (secon) { + AdventureManager.consoleMessage("[CustomNameplates] Successfully reconnect to SQL!"); + } else { + secon = true; + } + return true; + } catch (SQLException e) { + AdventureManager.consoleMessage("[CustomNameplates] Error! Failed to connect to SQL!"); + e.printStackTrace(); + close(); + return false; + } catch (ClassNotFoundException e) { + AdventureManager.consoleMessage("[CustomNameplates] Error! Failed to load JDBC driver"); + } + return false; + } + + public Connection getConnectionAndCheck() { + if (!canConnect()) { + return null; + } + try { + return getConnection(); + } catch (SQLException e) { + if (isfirstry) { + isfirstry = false; + close(); + return getConnectionAndCheck(); + } else { + isfirstry = true; + AdventureManager.consoleMessage("[CustomNameplates] Error! Failed to connect to SQL!"); + close(); + e.printStackTrace(); + return null; + } + } + } + + public Connection getConnection() throws SQLException { + if (ConfigManager.DatabaseConfig.enable_pool) { + return hikari.getConnection(); + } else { + return connection; + } + } + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + public boolean canConnect() { + try { + if (ConfigManager.DatabaseConfig.enable_pool) { + if (hikari == null) { + return setGlobalConnection(); + } + if (hikari.isClosed()) { + return setGlobalConnection(); + } + } else { + if (connection == null) { + return setGlobalConnection(); + } + if (connection.isClosed()) { + return setGlobalConnection(); + } + if (ConfigManager.DatabaseConfig.use_mysql) { + if (!connection.isValid(waitTimeOut)) { + secon = false; + return setGlobalConnection(); + } + } + } + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + return true; + } + + public void closeHikariConnection(Connection connection) { + if (!ConfigManager.DatabaseConfig.enable_pool) { + return; + } + try { + connection.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void close() { + try { + if (connection != null) { + connection.close(); + } + if (hikari != null) { + hikari.close(); + } + } catch (SQLException e) { + AdventureManager.consoleMessage("[CustomNameplates] Error! Failed to close SQL!"); + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..f36545c --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,47 @@ +config: + # Messages Language + lang: en + + # Your namespace + # "minecraft" is also + namespace: "nameplates" + + # Font Name + # Recommended not to set "default" when you are using "minecraft" as your namespace. + # You can use font like "nameplate" instead. + font: "default" + + # Customize the folder where png files should be generated. + # This is useful for those who want to keep their resource pack structure in order. + folder-path: 'font\nameplates\' + + # The initial character of all nameplates. + # 뀁 is the first character of Korean \ub001. + # You can specify any Unicode Character as you want. + start-char: '뀁' + + # The duration (in seconds) that the nameplate preview will last for. + preview-duration: 5 + + # The default nameplate. + # "none" represents no default nameplate. + default-nameplate: 'none' + + # Recommended to keep this true, otherwise players may see weird characters + # above their heads if they have not accepted the resource pack. + # This option supports proxy when using Mysql + show-after-load-resourcepack: true + + # Placeholder based prefix and suffix system. When enabled, it is recommended + # to use PlaceholderAPI to be able to use this feature to the fullest extent. + # keep it empty if you don't want to enable this feature. + prefix: '%vault_prefix%' + suffix: '' + + # Should the plugin hook into other plugins + integrations: + # When enabled, the plugin will be able to parse prefix and suffix + PlaceholderAPI: false + # When enabled, the plugin will automatically place the nameplates + # folder into the resource pack generated by ItemsAdder. + ItemsAdder: false \ No newline at end of file diff --git a/src/main/resources/database.yml b/src/main/resources/database.yml new file mode 100644 index 0000000..1197e54 --- /dev/null +++ b/src/main/resources/database.yml @@ -0,0 +1,28 @@ +settings: + # Valid Options: SQLite MYSQL + # Only MYSQL supports pool. + # Restart is a must when you change database configuration. + storage-mode: SQLite + use-pool: false + disable-async: false + +# MySQL settings +MySQL: + host: localhost + port: 3306 + user: root + password: password + database: minecraft + table-name: nameplates_data + property: + use-ssl: false + encoding: utf8 + timezone: '' + allowPublicKeyRetrieval: false + +# Connection pool settings +Pool-Settings: + maximum-pool-size: 10 + minimum-idle: 10 + maximum-lifetime: 180000 + idle-timeout: 60000 \ No newline at end of file diff --git a/src/main/resources/messages/messages_cn.yml b/src/main/resources/messages/messages_cn.yml new file mode 100644 index 0000000..950bcee --- /dev/null +++ b/src/main/resources/messages/messages_cn.yml @@ -0,0 +1,16 @@ +messages: + prefix: '[CustomNameplates] ' + no-perm: '你没有权限!' + lack-args: '参数不足!' + reload: '插件已重载!' + equip: '你已佩戴铭牌 {Nameplate} [点击预览]' + force-equip: '你已为玩家 {Player} 强制佩戴了铭牌 {Nameplate}' + unequip: '你已卸下铭牌!' + force-unequip: '你已强制卸下玩家 {Player} 的铭牌!' + preview: '你正在预览你的铭牌(切换到第三人称)' + not-exist: '那个铭牌不存在!' + not-online: '玩家 {Player} 不在线!' + no-console: '这个指令不能由控制台执行!' + not-available: '你还未拥有这个铭牌!' + available: '可用铭牌: {Nameplates}' + cooldown: '上一个预览还没结束!' \ No newline at end of file diff --git a/src/main/resources/messages/messages_en.yml b/src/main/resources/messages/messages_en.yml new file mode 100644 index 0000000..7ed62bc --- /dev/null +++ b/src/main/resources/messages/messages_en.yml @@ -0,0 +1,16 @@ +messages: + prefix: '[CustomNameplates] ' + no-perm: 'No Permission!' + lack-args: 'Insufficient parameters!' + reload: 'Reloaded!' + equip: 'Successfully equipped nameplate {Nameplate}. [click to preview]' + force-equip: 'Successfully equipped nameplate {Nameplate} to {Player}.' + unequip: 'You have removed your nameplate!' + force-unequip: 'Successfully removed {Player}''s nameplate!' + preview: 'Now previewing your nameplate (go to third person to view)!' + not-exist: 'This nameplate does not exist!' + not-online: 'Player {Player} is not online!' + no-console: 'This command can be only sent by player!' + not-available: 'This nameplate is currently not available!' + available: 'Available nameplates: {Nameplates}.' + cooldown: 'Previewing is still Ongoing!' \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..d311fe3 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,17 @@ +name: CustomNameplates +version: '${version}' +main: net.momirealms.customnameplates.CustomNameplates +api-version: 1.16 +authors: [ XiaoMoMi ] +depend: + - ProtocolLib + - PlaceholderAPI +softdepend: + - ItemsAdder + - Oraxen + - PlaceholderAPI +commands: + customnameplates: + usage: /customnameplates help + aliases: + - nameplates \ No newline at end of file diff --git a/src/main/resources/resources/cat.png b/src/main/resources/resources/cat.png new file mode 100644 index 0000000000000000000000000000000000000000..87268888e24e988ad736d9cb87bdfb3e16a17690 GIT binary patch literal 812 zcmV+{1JnG8P)(5!f$gkRAl4b!-ZgAg-{O z*ic!FTCjdV<=x~7%>4>d&jgmh3?MOEKyX9ljHwmqGFU8FRhc43P?&1K^Z~&=M!!_n z-XL%&ahb3{qV1QWr62S6ZA6QOwt(Q)1Sp;a1X%v7XL`Xf`663@YZ~G{y0*qsNdyJ~ z7EOeYIY0c^KM4|#0(Gx~)R6maTGG>DJ_KiezQFyVz|p@cpfpU(uM#cGhk%Cgf1FAb zp`t89MQqt`{Tu52I|MY4=uzNB(Eb!OUGW1UUf|KVLb*rk5bySa?!D-v!M{lPCP)C6 qPr2nYJt+>CA8G6p(TQLF7GMBun>h-hk%nym0000DSr z1<%~X^wgl##FWaylc_d9MF{~uA+9?YR32H~c6e#^_Sq$iD?N%mC0EU8?d|D0y|H`k zs^u59^p>UjE?PKm&zh;za~)7G`Y2Z_KUNtmUUJv3U@kp=5$j- zjsLpyTW031m|VJXQoOK;`2YX^Ia%sLL9Qza@(TuX@d1XI80ohR3`};OE{-7;x3)}s zUG!Lir`51yh6B^HYg-OZ_+Ee7>&V+Bx97ggJEJ98{pWd2M{ ylBhR%dc~5HvWDa zP`W|!ul|9X*MxeuysUIOylboU-|e@*buffEr5-xALu20j_^j%XcU$@rCUc8cXa*iz z5%g>~!~EPkf>-#2MHh4=EskmM{aJbF`6qcz4u`*5VtI>u*J#Y$e2e=>RboK)#zTu( zuXTDppK|4fNT>z_<8}GP$vRKxd`;9?;J|8fz$!}-S}R;Hw_j9WFA7KWn7#(uSrtfq z_Yh7}U$FJ9lbp+hjnxd>3fwJyBcB&)PEygY=DudMuz8W(7YBw{)&6ofG{PD`3E$uB zdY?f~z2V8prve|Y1WPRaV#N1AX=O714W=E3xQ-XfRYVzBwbtn0TJoSGXwR&P3>>MS z?y}05b$f2ww6%~$jPZVYOtz_PSb<=I=~;7ofeXt=n{%&v>+K6w;t34L{9No)Ub`FyraA^sS3j3^P6DSr z1<%~X^wgl##FWaylc_d9MF{~uA+G-ysC@eL_x~j6|7#8Z&rtaH```bSy1#z?{=Y)= z|56Qg7DgFq=|6w|egFRN|CdMq|NsB-+g7*cWT z&9v7=%?bjoom^g`tiHF@Vn6)7ziBtO+vR1`Cp?Qk?~_*axn@pkr0C~n)qjlt8}>6X zyewp3cs-|@r~7vEwuU#0 z{wBot9X=(n(t%C6=kgTZ2F07(H+neBW@U&sNJ=KmIjI+@bwSQ6G;6IiN6;g6wg~IK zV@Z5l+W+liSTO5Tbj;*LzJsfddn>LO^Mb{kI?YBpw@c2sUu; zu;YC2_rfPjh3_}N6!&XN6j(Rj61w^-jWvR6LfhQ*1AH6iN(d;d_VxeJV|_qW>%eba zhWNj45~Ob0vmfA8nX=>PmVd8aawW8gH2i+;w$<4t0WbILr^ zo&JlPwn>~bpVRPf+j6l}>AdCrl%OU({SQSOd1kz7o^W8!Dl6uF^*IY=yi-E8de22^hq@^SUpBd|)wnyJ$>O9< ves5|} zAP95-wf+AeE(2~7lh=c8+e1C&Ez69643y7bKILB#fM$uf z031l|LE!j8Zqj?a8>fM{X8@jVy%7M_iIpb*0LsPz=H)nNwf=rw_$L4 zcwyQr0=t0i8cCA+5`*F|D4uZ8M-{MJWMl<032Y`(6tvd+$*@^~qYNj0?~VFVvP;;% z8WG?ff+!~8nTOQ8EV6eE8@EuZ0^*V9O zIrAuYj17Fi%lM7$8hzjd?KzWar&J)O-}=aD*}BY