9
0
mirror of https://github.com/Xiao-MoMi/Custom-Nameplates.git synced 2025-12-25 09:59:16 +00:00

1.0 - SNAPSHOT

This commit is contained in:
Xiao-MoMi
2022-06-21 22:43:16 +08:00
parent 25d150bffe
commit 025bba917b
31 changed files with 2083 additions and 0 deletions

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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("<red>[CustomNameplates] Error! Failed to enable Data Manager! Disabling plugin...</red>");
instance.getPluginLoader().disablePlugin(instance);
return;
}
//启动完成
AdventureManager.consoleMessage("<gradient:#DDE4FF:#8DA2EE>[CustomNameplates]</gradient> <color:#F5F5F5>Plugin has been enabled! Author: XiaoMoMi");
}
@Override
public void onDisable() {
SqlHandler.saveAll();
SqlHandler.close();
//清除缓存实体
Execute.pCache.forEach(Entity::remove);
//卸载完成
AdventureManager.consoleMessage("<gradient:#DDE4FF:#8DA2EE>[CustomNameplates]</gradient> <color:#F5F5F5>Plugin has been disabled! Author: XiaoMoMi");
//关闭adventure
if(adventure != null) {
adventure.close();
adventure = null;
}
}
}

View File

@@ -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("<gradient:#DDE4FF:#8DA2EE>[CustomNameplates]</gradient> " + "<color:#F5F5F5>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("<gradient:#DDE4FF:#8DA2EE>[CustomNameplates]</gradient> " + "<color:#F5F5F5>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);
}
}

View File

@@ -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<Player, Long> coolDown;
{
coolDown = new HashMap<>();
}
public static List<Entity> 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("<red>[CustomNameplates] Error! Failed to preview for "+ player.getName()+"</red>");
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,"<color:#87CEFA>/nameplates help - <color:#7FFFAA>show the command list");
AdventureManager.playerMessage(player,"<color:#87CEFA>/nameplates reload - <color:#7FFFAA>reload the configuration");
AdventureManager.playerMessage(player,"<color:#87CEFA>/nameplates equip <nameplate> - <color:#7FFFAA>equip a specified nameplate");
AdventureManager.playerMessage(player,"<color:#87CEFA>/nameplates forceequip <player> <nameplate> - <color:#7FFFAA>force a player to equip a specified nameplate");
AdventureManager.playerMessage(player,"<color:#87CEFA>/nameplates unequip - <color:#7FFFAA>unequip your nameplate");
AdventureManager.playerMessage(player,"<color:#87CEFA>/nameplates forceunequip - <color:#7FFFAA>force unequip a player's nameplate");
AdventureManager.playerMessage(player,"<color:#87CEFA>/nameplates preview - <color:#7FFFAA>preview your nameplate");
AdventureManager.playerMessage(player,"<color:#87CEFA>/nameplates list - <color:#7FFFAA>list your available nameplates");
}
}else {
AdventureManager.consoleMessage("<color:#87CEFA>/nameplates help - <color:#7FFFAA>show the command list");
AdventureManager.consoleMessage("<color:#87CEFA>/nameplates reload - <color:#7FFFAA>reload the configuration");
AdventureManager.consoleMessage("<color:#87CEFA>/nameplates equip <nameplate> - <color:#7FFFAA>equip a specified nameplate");
AdventureManager.consoleMessage("<color:#87CEFA>/nameplates forceequip <player> <nameplate> - <color:#7FFFAA>force a player to equip a specified nameplate");
AdventureManager.consoleMessage("<color:#87CEFA>/nameplates unequip - <color:#7FFFAA>unequip your nameplate");
AdventureManager.consoleMessage("<color:#87CEFA>/nameplates forceunequip - <color:#7FFFAA>force unequip a player's nameplate");
AdventureManager.consoleMessage("<color:#87CEFA>/nameplates preview - <color:#7FFFAA>preview your nameplate");
AdventureManager.consoleMessage("<color:#87CEFA>/nameplates list - <color:#7FFFAA>list your available nameplates");
}
return true;
}
}
return true;
}
}

View File

@@ -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<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
if(1 == args.length){
List<String> 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<String> 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<String> online_players(){
List<String> online = new ArrayList<>();
Bukkit.getOnlinePlayers().forEach((player -> online.add(player.getName())));
return online;
}
private List<String> availableNameplates(CommandSender sender){
List<String> 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<String> nameplates(){
return new ArrayList<>(this.plugin.getResourceManager().caches.keySet());
}
}

View File

@@ -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<UUID, PlayerData> 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("<gradient:#DDE4FF:#8DA2EE>[CustomNameplates]</gradient> <color:#00CED1>Storage Mode - MYSQL");
}else {
AdventureManager.consoleMessage("<gradient:#DDE4FF:#8DA2EE>[CustomNameplates]</gradient> <color:#00CED1>Storage Mode - SQLite");
}
if (SqlHandler.connect()) {
if (ConfigManager.DatabaseConfig.use_mysql) {
SqlHandler.getWaitTimeOut();
}
SqlHandler.createTable();
} else {
AdventureManager.consoleMessage("<red>//DATA storage ERROR//</red>");
return false;
}
return true;
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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); //总长还需加上字符间距
}
}

View File

@@ -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<InternalStructure> 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());
}
}

View File

@@ -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());
}
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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<String, FontCache> 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("<gradient:#DDE4FF:#8DA2EE>[CustomNameplates]</gradient> <color:#F5F5F5>Failed to detect resources folder! Generating default resources...");
if (!r_file.mkdir()) {
AdventureManager.consoleMessage("<red>[CustomNameplates] Error! Failed to create resources folder...</red>");
return;
}
saveDefaultResources();
}
//获取资源文件夹下的所有png文件
File[] pngFiles = r_file.listFiles(file -> file.getName().endsWith(".png"));
if (pngFiles == null) {
AdventureManager.consoleMessage("<red>[CustomNameplates] Error! No png files detected in resource folder...</red>");
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("<red>[CustomNameplates] Error! Failed to generate resource pack folders...</red>");
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("<red>[CustomNameplates] Error! Failed to copy png files to resource pack...</red>");
}
}
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("<red>[CustomNameplates] Error! Failed to copy space_split.png to resource pack...</red>");
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("<red>[CustomNameplates] Error! Failed to generate font json...</red>");
return;
}
//资源包生成成功提示
AdventureManager.consoleMessage("<gradient:#DDE4FF:#8DA2EE>[CustomNameplates]</gradient> <color:#F5F5F5>Resource pack has been successfully generated! <color:#3CB371>" + this.caches.size() + " <color:#F5F5F5>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("<red>[CustomNameplates] Error! Failed to copy files to ItemsAdder...</red>");
}
}
}
/*
保存插件预设资源
*/
private void saveDefaultResources() {
List<String> 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("<gradient:#DDE4FF:#8DA2EE>[CustomNameplates]</gradient> <color:#F5F5F5>Successfully copy files to ItemsAdder...");
}catch (IOException e){
e.printStackTrace();
AdventureManager.consoleMessage("<red>[CustomNameplates] Error! Failed to delete generated folder...</red>" );
}
}
}
/*
获取铭牌的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("<red>[CustomNameplates] Invalid Color of " + nameplate + "</red>");
}
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<JsonObject> getNegativeFontEnums() {
ArrayList<JsonObject> 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);
}
}

View File

@@ -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("");
}
}

View File

@@ -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<String, NameplatesTeam> 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);
}
}

View File

@@ -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("<gradient:#DDE4FF:#8DA2EE>[CustomNameplates]</gradient> <color:#F5F5F5>Successfully reconnect to SQL!");
} else {
secon = true;
}
return true;
} catch (SQLException e) {
AdventureManager.consoleMessage("<red>[CustomNameplates] Error! Failed to connect to SQL!</red>");
e.printStackTrace();
close();
return false;
} catch (ClassNotFoundException e) {
AdventureManager.consoleMessage("<red>[CustomNameplates] Error! Failed to load JDBC driver</red>");
}
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("<red>[CustomNameplates] Error! Failed to connect to SQL!</red>");
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("<red>[CustomNameplates] Error! Failed to close SQL!</red>");
e.printStackTrace();
}
}
}