diff --git a/src/main/java/pw/yumc/YumCore/bukkit/compatible/C.java b/src/main/java/pw/yumc/YumCore/bukkit/compatible/C.java index 4d75d34..e14c81e 100644 --- a/src/main/java/pw/yumc/YumCore/bukkit/compatible/C.java +++ b/src/main/java/pw/yumc/YumCore/bukkit/compatible/C.java @@ -1,14 +1,19 @@ package pw.yumc.YumCore.bukkit.compatible; import java.lang.reflect.Constructor; +import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; import java.util.UUID; +import java.util.logging.Level; import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.OfflinePlayer; import org.bukkit.Server; +import org.bukkit.World; +import org.json.simple.JSONObject; import com.google.common.base.Charsets; @@ -21,6 +26,167 @@ import pw.yumc.YumCore.bukkit.Log; * @author 喵♂呜 */ public class C { + private static Class nmsChatSerializer; + private static Class nmsIChatBaseComponent; + private static Class packetType; + private static Class packetActions; + private static Class packetTitle; + + private static Method getHandle; + + private static String version; + private static Field playerConnection; + private static Method sendPacket; + + static { + try { + version = Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3]; + final boolean newversion = Integer.parseInt(version.split("_")[1]) > 7; + nmsChatSerializer = Class.forName(a(newversion ? "ChatSerializer" : "IChatBaseComponent$ChatSerializer")); + nmsIChatBaseComponent = Class.forName(a("IChatBaseComponent")); + packetType = Class.forName(a("PacketPlayOutChat")); + packetActions = Class.forName(a(newversion ? "EnumTitleAction" : "PacketPlayOutTitle$EnumTitleAction")); + packetTitle = Class.forName(a("PacketPlayOutTitle")); + final Class typeCraftPlayer = Class.forName(a("CraftPlayer")); + final Class typeNMSPlayer = Class.forName(a("EntityPlayer")); + final Class typePlayerConnection = Class.forName(a("PlayerConnection")); + getHandle = typeCraftPlayer.getMethod("getHandle"); + playerConnection = typeNMSPlayer.getField("playerConnection"); + sendPacket = typePlayerConnection.getMethod("sendPacket", Class.forName(a("Packet"))); + } catch (final Exception e) { + Log.warning(Player.class.getSimpleName() + "兼容性工具初始化失败 可能造成部分功能不可用!"); + } + } + + public static String a(final String str) { + return "net.minecraft.server." + version + "." + str; + } + + public static class ActionBar { + /** + * 公告发送ActionBar + * + * @param message + * 需要发送的消息 + */ + public static void broadcast(final String message) { + for (final org.bukkit.entity.Player player : C.Player.getOnlinePlayers()) { + send(player, message); + } + } + + /** + * 公告发送ActionBar + * + * @param message + * 需要发送的消息 + * @param times + * 需要显示的时间 + */ + public static void broadcast(final String message, final int times) { + new Thread(new Runnable() { + @Override + public void run() { + int time = times; + do { + for (final org.bukkit.entity.Player player : C.Player.getOnlinePlayers()) { + send(player, message); + } + try { + Thread.sleep(1000); + } catch (final InterruptedException e) { + } + time--; + } while (time > 0); + } + }).start(); + } + + /** + * 公告发送ActionBar(分世界) + * + * @param world + * 需要发送的世界 + * @param message + * 需要发送的消息 + * @param times + * 需要显示的时间 + */ + public static void broadcast(final World world, final String message, final int times) { + new Thread(new Runnable() { + @Override + public void run() { + int time = times; + do { + for (final org.bukkit.entity.Player player : C.Player.getOnlinePlayers()) { + if (player.getWorld().getName().equalsIgnoreCase(world.getName())) { + send(player, message); + } + } + try { + Thread.sleep(1000); + } catch (final InterruptedException e) { + } + time--; + } while (time > 0); + + } + }).start();; + } + + /** + * 给玩家发送ActionBar消息 + * + * @param receivingPacket + * 接受信息的玩家 + * @param msg + * ActionBar信息 + */ + public static void send(final org.bukkit.entity.Player receivingPacket, final String msg) { + Object packet = null; + try { + final Object serialized = nmsChatSerializer.getMethod("a", String.class).invoke(null, "{\"text\":\"" + ChatColor.translateAlternateColorCodes('&', JSONObject.escape(msg)) + "\"}"); + if (!version.contains("1_7")) { + packet = packetType.getConstructor(nmsIChatBaseComponent, byte.class).newInstance(serialized, (byte) 2); + } else { + packet = packetType.getConstructor(nmsIChatBaseComponent, int.class).newInstance(serialized, 2); + } + final Object player = getHandle.invoke(receivingPacket); + final Object connection = playerConnection.get(player); + sendPacket.invoke(connection, packet); + } catch (final Exception ex) { + Log.log(Level.SEVERE, "ActionBar发包错误 %s " + version, ex); + } + } + + /** + * 给玩家发送ActionBar消息 + * + * @param receivingPacket + * 接受信息的玩家 + * @param msg + * 需要发送的消息 + * @param times + * 需要显示的时间 + */ + public static void send(final org.bukkit.entity.Player receivingPacket, final String msg, final int times) { + new Thread(new Runnable() { + @Override + public void run() { + int time = times; + do { + send(receivingPacket, msg); + try { + Thread.sleep(1000); + } catch (final InterruptedException e) { + } + time--; + } while (time > 0); + } + }).start(); + } + } + /** * Bukkit Player兼容类 * @@ -97,4 +263,84 @@ public class C { } } } + + public static class Title { + /** + * 重置玩家的Title + * + * @param recoverPlayer + * 接受的玩家 + * @throws Exception + * 异常 + */ + public static void reset(final Player recoverPlayer) throws Exception { + // Send timings first + final Object player = getHandle.invoke(recoverPlayer); + final Object connection = playerConnection.get(player); + final Object[] actions = packetActions.getEnumConstants(); + final Object packet = packetTitle.getConstructor(packetActions, nmsIChatBaseComponent).newInstance(actions[4], null); + sendPacket.invoke(connection, packet); + } + + /** + * 发送Titile(默认时间 1 2 1) + * + * @param receivingPacket + * 接受信息的玩家 + * @param title + * 标题 + * @param subtitle + * 子标题 + */ + public static void send(final Player receivingPacket, final String title, final String subtitle) { + send(receivingPacket, title, subtitle, 1, 2, 1); + } + + /** + * 发送Titile + * + * @param receivingPacket + * 接受信息的玩家 + * @param title + * 标题 + * @param subtitle + * 子标题 + * @param fadeInTime + * 淡入时间 + * @param stayTime + * 持续时间 + * @param fadeOutTime + * 淡出时间 + */ + public static void send(final Player receivingPacket, final String title, final String subtitle, final int fadeInTime, final int stayTime, final int fadeOutTime) { + if (packetTitle != null) { + try { + // First reset previous settings + reset(receivingPacket); + // Send timings first + final Object player = getHandle.invoke(receivingPacket); + final Object connection = playerConnection.get(player); + final Object[] actions = packetActions.getEnumConstants(); + Object packet = null; + // Send if set + if ((fadeInTime != -1) && (fadeOutTime != -1) && (stayTime != -1)) { + packet = packetTitle.getConstructor(packetActions, nmsIChatBaseComponent, Integer.TYPE, Integer.TYPE, Integer.TYPE).newInstance(actions[2], null, fadeInTime * 20, stayTime * 20, fadeOutTime * 20); + sendPacket.invoke(connection, packet); + } + // Send title + Object serialized = nmsChatSerializer.getMethod("a", String.class).invoke(null, "{text:\"" + ChatColor.translateAlternateColorCodes('&', title) + "\"}"); + packet = packetTitle.getConstructor(packetActions, nmsIChatBaseComponent).newInstance(actions[0], serialized); + sendPacket.invoke(connection, packet); + if (subtitle != "") { + // Send subtitle if present + serialized = nmsChatSerializer.getMethod("a", String.class).invoke(null, "{text:\"" + ChatColor.translateAlternateColorCodes('&', subtitle) + "\"}"); + packet = packetTitle.getConstructor(packetActions, nmsIChatBaseComponent).newInstance(actions[1], serialized); + sendPacket.invoke(connection, packet); + } + } catch (final Throwable e) { + e.printStackTrace(); + } + } + } + } }