From 808a24085bb71d73d86b7a0753b7ce9de0c9f095 Mon Sep 17 00:00:00 2001 From: Jedediah Smith Date: Fri, 20 Jan 2017 03:41:43 -0500 Subject: [PATCH] Custom text rendering API --- Bukkit/0097-SportBukkit.patch | 158 ++++ .../0001-Import-necessary-NMS-classes.patch | 223 ++++++ CraftBukkit/0172-SportBukkit.patch | 713 ++++++++++++++++++ .../main/java/org/bukkit/BukkitRuntime.java | 5 + .../main/java/org/bukkit/ServerModule.java | 9 +- .../bukkit/command/ConsoleCommandSender.java | 3 +- .../main/java/org/bukkit/plugin/Plugin.java | 8 + .../java/org/bukkit/plugin/PluginBase.java | 9 + .../java/org/bukkit/plugin/PluginModule.java | 2 + .../nms-patches/LoginListener.patch | 3 +- .../nms-patches/NetworkManager.patch | 16 +- .../nms-patches/PacketDataSerializer.patch | 30 +- .../nms-patches/PacketEncoder.patch | 7 +- .../nms-patches/PacketPlayOutBoss.patch | 31 + .../nms-patches/PacketPlayOutChat.patch | 64 +- .../PacketPlayOutCombatEvent.patch | 33 + .../nms-patches/PacketPlayOutPlayerInfo.patch | 89 ++- .../PacketPlayOutPlayerListHeaderFooter.patch | 48 +- .../nms-patches/PacketPlayOutTitle.patch | 56 +- .../nms-patches/PlayerConnection.patch | 67 +- .../org/bukkit/craftbukkit/CraftServer.java | 22 + .../craftbukkit/entity/CraftPlayer.java | 50 +- .../craftbukkit/protocol/Renderable.java | 8 + .../protocol/RenderableComponent.java | 69 ++ 24 files changed, 1533 insertions(+), 190 deletions(-) create mode 100644 snapshot/CraftBukkit/nms-patches/PacketPlayOutBoss.patch create mode 100644 snapshot/CraftBukkit/nms-patches/PacketPlayOutCombatEvent.patch create mode 100644 snapshot/CraftBukkit/src/main/java/org/bukkit/craftbukkit/protocol/Renderable.java create mode 100644 snapshot/CraftBukkit/src/main/java/org/bukkit/craftbukkit/protocol/RenderableComponent.java diff --git a/Bukkit/0097-SportBukkit.patch b/Bukkit/0097-SportBukkit.patch index 953a681f..d2a25668 100644 --- a/Bukkit/0097-SportBukkit.patch +++ b/Bukkit/0097-SportBukkit.patch @@ -12,3 +12,161 @@ diff --git a/README.md b/README.md ====== A plugin API for [Minecraft](https://minecraft.net/) servers, currently maintained by [SpigotMC](http://www.spigotmc.org/). +diff --git a/src/main/java/org/bukkit/BukkitRuntime.java b/src/main/java/org/bukkit/BukkitRuntime.java +--- a/src/main/java/org/bukkit/BukkitRuntime.java ++++ b/src/main/java/org/bukkit/BukkitRuntime.java +@@ -8,6 +8,7 @@ import org.bukkit.potion.PotionBrewRegistry; + import org.bukkit.potion.PotionEffectRegistry; + import org.bukkit.registry.Key; + import org.bukkit.geometry.VectorFactory; ++import tc.oc.minecraft.api.text.TextRenderContext; + + public interface BukkitRuntime { + +@@ -39,4 +40,8 @@ public interface BukkitRuntime { + PotionBrewRegistry potionRegistry(); + + PotionEffectRegistry potionEffectRegistry(); ++ ++ default TextRenderContext textRenderContext() { ++ return (text, viewer) -> text; ++ } + } +diff --git a/src/main/java/org/bukkit/ServerModule.java b/src/main/java/org/bukkit/ServerModule.java +--- a/src/main/java/org/bukkit/ServerModule.java ++++ b/src/main/java/org/bukkit/ServerModule.java +@@ -16,8 +16,10 @@ import org.bukkit.plugin.messaging.Messenger; + import org.bukkit.scheduler.BukkitScheduler; + import org.bukkit.scoreboard.ScoreboardManager; + import tc.oc.inject.SingletonModule; ++import tc.oc.minecraft.api.command.Console; + import tc.oc.minecraft.api.plugin.PluginFinder; + import tc.oc.minecraft.api.server.LocalServer; ++import tc.oc.minecraft.api.text.TextRenderContext; + + /** + * Bindings for things that belong to a {@link Server}. +@@ -35,7 +37,7 @@ public class ServerModule extends SingletonModule { + bind(tc.oc.minecraft.api.server.Server.class).to(LocalServer.class); + bind(LocalServer.class).to(Server.class); + bind(BukkitRuntime.class).to(Server.class); +- bind(tc.oc.minecraft.api.command.ConsoleCommandSender.class).to(ConsoleCommandSender.class); ++ bind(Console.class).to(ConsoleCommandSender.class); + bind(PluginFinder.class).to(PluginManager.class); + } + +@@ -85,6 +87,11 @@ public class ServerModule extends SingletonModule { + } + + @Provides ++ TextRenderContext textRenderContext(Server server) { ++ return server.textRenderContext(); ++ } ++ ++ @Provides + Collection worlds(Server server) { + return server.worldsById().values(); + } +diff --git a/src/main/java/org/bukkit/command/ConsoleCommandSender.java b/src/main/java/org/bukkit/command/ConsoleCommandSender.java +--- a/src/main/java/org/bukkit/command/ConsoleCommandSender.java ++++ b/src/main/java/org/bukkit/command/ConsoleCommandSender.java +@@ -1,6 +1,7 @@ + package org.bukkit.command; + + import org.bukkit.conversations.Conversable; ++import tc.oc.minecraft.api.command.Console; + +-public interface ConsoleCommandSender extends CommandSender, Conversable, tc.oc.minecraft.api.command.ConsoleCommandSender { ++public interface ConsoleCommandSender extends CommandSender, Conversable, Console { + } +diff --git a/src/main/java/org/bukkit/plugin/Plugin.java b/src/main/java/org/bukkit/plugin/Plugin.java +--- a/src/main/java/org/bukkit/plugin/Plugin.java ++++ b/src/main/java/org/bukkit/plugin/Plugin.java +@@ -2,7 +2,10 @@ package org.bukkit.plugin; + + import java.io.File; + import java.io.InputStream; ++import java.util.Collection; ++import java.util.Collections; + import java.util.logging.Logger; ++import javax.inject.Provider; + + import org.bukkit.Server; + import org.bukkit.command.TabExecutor; +@@ -11,6 +14,7 @@ import org.bukkit.event.EventRegistry; + import org.bukkit.generator.ChunkGenerator; + + import com.avaje.ebean.EbeanServer; ++import tc.oc.minecraft.api.text.TextRenderer; + + /** + * Represents a Plugin +@@ -98,6 +102,10 @@ public interface Plugin extends TabExecutor, tc.oc.minecraft.api.plugin.Plugin, + */ + EventRegistry eventRegistry(); + ++ default Collection> textRenderers() { ++ return Collections.emptyList(); ++ } ++ + /** + * Returns the Server instance currently running this plugin + * +diff --git a/src/main/java/org/bukkit/plugin/PluginBase.java b/src/main/java/org/bukkit/plugin/PluginBase.java +--- a/src/main/java/org/bukkit/plugin/PluginBase.java ++++ b/src/main/java/org/bukkit/plugin/PluginBase.java +@@ -1,5 +1,6 @@ + package org.bukkit.plugin; + ++import java.util.Collection; + import java.util.Set; + import javax.inject.Inject; + import javax.inject.Provider; +@@ -9,6 +10,7 @@ import org.bukkit.event.EventRegistry; + import org.bukkit.permissions.Permission; + import tc.oc.minecraft.api.event.ListenerContext; + import tc.oc.exception.ExceptionHandler; ++import tc.oc.minecraft.api.text.TextRenderer; + + /** + * Represents a base {@link Plugin} +@@ -25,6 +27,7 @@ public abstract class PluginBase implements Plugin { + + @Inject private Set permissions; + @Inject private Provider listenerContext; ++ @Inject private Provider>> textRenderers; + + protected void assertInjected() { + if(injector == null) { +@@ -50,6 +53,12 @@ public abstract class PluginBase implements Plugin { + return exceptionHandler; + } + ++ @Override ++ public Collection> textRenderers() { ++ assertInjected(); ++ return textRenderers.get(); ++ } ++ + protected final void preEnable() { + permissions.forEach(pluginManager::addPermission); + listenerContext.get().enable(); +diff --git a/src/main/java/org/bukkit/plugin/PluginModule.java b/src/main/java/org/bukkit/plugin/PluginModule.java +--- a/src/main/java/org/bukkit/plugin/PluginModule.java ++++ b/src/main/java/org/bukkit/plugin/PluginModule.java +@@ -11,6 +11,7 @@ import tc.oc.minecraft.api.event.ListenerBinder; + import tc.oc.exception.ExceptionHandler; + import tc.oc.minecraft.api.plugin.PluginDescription; + import tc.oc.minecraft.api.scheduler.Scheduler; ++import tc.oc.minecraft.api.text.TextRendererBinder; + + /** + * Bindings for things belonging to a particular {@link Plugin}. +@@ -26,6 +27,7 @@ public class PluginModule extends ProtectedModule { + // Ensure these collections have bindings + new PermissionBinder(binder()); + new ListenerBinder(binder()); ++ new TextRendererBinder(binder()); + + bind(tc.oc.minecraft.api.plugin.Plugin.class).to(Plugin.class); + bind(PluginDescription.class).to(PluginDescriptionFile.class); diff --git a/CraftBukkit/0001-Import-necessary-NMS-classes.patch b/CraftBukkit/0001-Import-necessary-NMS-classes.patch index df9db714..11d710b2 100644 --- a/CraftBukkit/0001-Import-necessary-NMS-classes.patch +++ b/CraftBukkit/0001-Import-necessary-NMS-classes.patch @@ -3186,6 +3186,146 @@ index 0000000..9d23a64 + packetlistenerplayout.a(this); + } +} +diff --git a/src/main/java/net/minecraft/server/PacketPlayOutBoss.java b/src/main/java/net/minecraft/server/PacketPlayOutBoss.java +new file mode 100644 +index 0000000..ba14c69 +--- /dev/null ++++ b/src/main/java/net/minecraft/server/PacketPlayOutBoss.java +@@ -0,0 +1,134 @@ ++package net.minecraft.server; ++ ++import java.io.IOException; ++import java.util.UUID; ++ ++public class PacketPlayOutBoss implements Packet { ++ ++ private UUID a; ++ private PacketPlayOutBoss.Action b; ++ private IChatBaseComponent c; ++ private float d; ++ private BossBattle.BarColor e; ++ private BossBattle.BarStyle f; ++ private boolean g; ++ private boolean h; ++ private boolean i; ++ ++ public PacketPlayOutBoss() {} ++ ++ public PacketPlayOutBoss(PacketPlayOutBoss.Action packetplayoutboss_action, BossBattle bossbattle) { ++ this.b = packetplayoutboss_action; ++ this.a = bossbattle.d(); ++ this.c = bossbattle.e(); ++ this.d = bossbattle.getProgress(); ++ this.e = bossbattle.g(); ++ this.f = bossbattle.h(); ++ this.g = bossbattle.i(); ++ this.h = bossbattle.j(); ++ this.i = bossbattle.k(); ++ } ++ ++ public void a(PacketDataSerializer packetdataserializer) throws IOException { ++ this.a = packetdataserializer.i(); ++ this.b = (PacketPlayOutBoss.Action) packetdataserializer.a(PacketPlayOutBoss.Action.class); ++ switch (this.b) { ++ case ADD: ++ this.c = packetdataserializer.f(); ++ this.d = packetdataserializer.readFloat(); ++ this.e = (BossBattle.BarColor) packetdataserializer.a(BossBattle.BarColor.class); ++ this.f = (BossBattle.BarStyle) packetdataserializer.a(BossBattle.BarStyle.class); ++ this.a(packetdataserializer.readUnsignedByte()); ++ ++ case REMOVE: ++ default: ++ break; ++ ++ case UPDATE_PCT: ++ this.d = packetdataserializer.readFloat(); ++ break; ++ ++ case UPDATE_NAME: ++ this.c = packetdataserializer.f(); ++ break; ++ ++ case UPDATE_STYLE: ++ this.e = (BossBattle.BarColor) packetdataserializer.a(BossBattle.BarColor.class); ++ this.f = (BossBattle.BarStyle) packetdataserializer.a(BossBattle.BarStyle.class); ++ break; ++ ++ case UPDATE_PROPERTIES: ++ this.a(packetdataserializer.readUnsignedByte()); ++ } ++ ++ } ++ ++ private void a(int i) { ++ this.g = (i & 1) > 0; ++ this.h = (i & 2) > 0; ++ this.i = (i & 2) > 0; ++ } ++ ++ public void b(PacketDataSerializer packetdataserializer) throws IOException { ++ packetdataserializer.a(this.a); ++ packetdataserializer.a((Enum) this.b); ++ switch (this.b) { ++ case ADD: ++ packetdataserializer.a(this.c); ++ packetdataserializer.writeFloat(this.d); ++ packetdataserializer.a((Enum) this.e); ++ packetdataserializer.a((Enum) this.f); ++ packetdataserializer.writeByte(this.j()); ++ ++ case REMOVE: ++ default: ++ break; ++ ++ case UPDATE_PCT: ++ packetdataserializer.writeFloat(this.d); ++ break; ++ ++ case UPDATE_NAME: ++ packetdataserializer.a(this.c); ++ break; ++ ++ case UPDATE_STYLE: ++ packetdataserializer.a((Enum) this.e); ++ packetdataserializer.a((Enum) this.f); ++ break; ++ ++ case UPDATE_PROPERTIES: ++ packetdataserializer.writeByte(this.j()); ++ } ++ ++ } ++ ++ private int j() { ++ int i = 0; ++ ++ if (this.g) { ++ i |= 1; ++ } ++ ++ if (this.h) { ++ i |= 2; ++ } ++ ++ if (this.i) { ++ i |= 2; ++ } ++ ++ return i; ++ } ++ ++ public void a(PacketListenerPlayOut packetlistenerplayout) { ++ packetlistenerplayout.a(this); ++ } ++ ++ public static enum Action { ++ ++ ADD, REMOVE, UPDATE_PCT, UPDATE_NAME, UPDATE_STYLE, UPDATE_PROPERTIES; ++ ++ private Action() {} ++ } ++} diff --git a/src/main/java/net/minecraft/server/PacketPlayOutChat.java b/src/main/java/net/minecraft/server/PacketPlayOutChat.java new file mode 100644 index 0000000..24462f1 @@ -3274,6 +3414,89 @@ index 0000000..1678628 + packetlistenerplayout.a(this); + } +} +diff --git a/src/main/java/net/minecraft/server/PacketPlayOutCombatEvent.java b/src/main/java/net/minecraft/server/PacketPlayOutCombatEvent.java +new file mode 100644 +index 0000000..46885f9 +--- /dev/null ++++ b/src/main/java/net/minecraft/server/PacketPlayOutCombatEvent.java +@@ -0,0 +1,77 @@ ++package net.minecraft.server; ++ ++import java.io.IOException; ++ ++public class PacketPlayOutCombatEvent implements Packet { ++ ++ public PacketPlayOutCombatEvent.EnumCombatEventType a; ++ public int b; ++ public int c; ++ public int d; ++ public IChatBaseComponent e; ++ ++ public PacketPlayOutCombatEvent() {} ++ ++ public PacketPlayOutCombatEvent(CombatTracker combattracker, PacketPlayOutCombatEvent.EnumCombatEventType packetplayoutcombatevent_enumcombateventtype) { ++ this(combattracker, packetplayoutcombatevent_enumcombateventtype, true); ++ } ++ ++ public PacketPlayOutCombatEvent(CombatTracker combattracker, PacketPlayOutCombatEvent.EnumCombatEventType packetplayoutcombatevent_enumcombateventtype, boolean flag) { ++ this.a = packetplayoutcombatevent_enumcombateventtype; ++ EntityLiving entityliving = combattracker.c(); ++ ++ switch (packetplayoutcombatevent_enumcombateventtype) { ++ case END_COMBAT: ++ this.d = combattracker.f(); ++ this.c = entityliving == null ? -1 : entityliving.getId(); ++ break; ++ ++ case ENTITY_DIED: ++ this.b = combattracker.h().getId(); ++ this.c = entityliving == null ? -1 : entityliving.getId(); ++ if (flag) { ++ this.e = combattracker.getDeathMessage(); ++ } else { ++ this.e = new ChatComponentText(""); ++ } ++ } ++ ++ } ++ ++ public void a(PacketDataSerializer packetdataserializer) throws IOException { ++ this.a = (PacketPlayOutCombatEvent.EnumCombatEventType) packetdataserializer.a(PacketPlayOutCombatEvent.EnumCombatEventType.class); ++ if (this.a == PacketPlayOutCombatEvent.EnumCombatEventType.END_COMBAT) { ++ this.d = packetdataserializer.g(); ++ this.c = packetdataserializer.readInt(); ++ } else if (this.a == PacketPlayOutCombatEvent.EnumCombatEventType.ENTITY_DIED) { ++ this.b = packetdataserializer.g(); ++ this.c = packetdataserializer.readInt(); ++ this.e = packetdataserializer.f(); ++ } ++ ++ } ++ ++ public void b(PacketDataSerializer packetdataserializer) throws IOException { ++ packetdataserializer.a((Enum) this.a); ++ if (this.a == PacketPlayOutCombatEvent.EnumCombatEventType.END_COMBAT) { ++ packetdataserializer.d(this.d); ++ packetdataserializer.writeInt(this.c); ++ } else if (this.a == PacketPlayOutCombatEvent.EnumCombatEventType.ENTITY_DIED) { ++ packetdataserializer.d(this.b); ++ packetdataserializer.writeInt(this.c); ++ packetdataserializer.a(this.e); ++ } ++ ++ } ++ ++ public void a(PacketListenerPlayOut packetlistenerplayout) { ++ packetlistenerplayout.a(this); ++ } ++ ++ public static enum EnumCombatEventType { ++ ++ ENTER_COMBAT, END_COMBAT, ENTITY_DIED; ++ ++ private EnumCombatEventType() {} ++ } ++} diff --git a/src/main/java/net/minecraft/server/PacketPlayOutCustomSoundEffect.java b/src/main/java/net/minecraft/server/PacketPlayOutCustomSoundEffect.java new file mode 100644 index 0000000..8a19c73 diff --git a/CraftBukkit/0172-SportBukkit.patch b/CraftBukkit/0172-SportBukkit.patch index f7761a75..26e210e0 100644 --- a/CraftBukkit/0172-SportBukkit.patch +++ b/CraftBukkit/0172-SportBukkit.patch @@ -12,3 +12,716 @@ diff --git a/README.md b/README.md ====== An implementation of the [Bukkit](https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit) plugin API for [Minecraft](https://minecraft.net/) servers, currently maintained by [SpigotMC](http://www.spigotmc.org/). +diff --git a/src/main/java/net/minecraft/server/LoginListener.java b/src/main/java/net/minecraft/server/LoginListener.java +--- a/src/main/java/net/minecraft/server/LoginListener.java ++++ b/src/main/java/net/minecraft/server/LoginListener.java +@@ -221,6 +221,7 @@ public class LoginListener implements PacketLoginInListener, ITickable { + String playerName = i.getName(); + java.net.InetAddress address = ((java.net.InetSocketAddress) networkManager.getSocketAddress()).getAddress(); + java.util.UUID uniqueId = i.getId(); ++ networkManager.playerId = uniqueId; + final org.bukkit.craftbukkit.CraftServer server = LoginListener.this.server.server; + AsyncPlayerPreLoginEvent asyncEvent = new AsyncPlayerPreLoginEvent(playerName, address, uniqueId); + +diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java +--- a/src/main/java/net/minecraft/server/NetworkManager.java ++++ b/src/main/java/net/minecraft/server/NetworkManager.java +@@ -71,8 +71,10 @@ public class NetworkManager extends SimpleChannelInboundHandler> { + public SocketAddress l; + public java.util.UUID spoofedUUID; + public com.mojang.authlib.properties.Property[] spoofedProfile; ++ ++ public int protocolVersion = Protocol.LATEST; ++ public java.util.UUID playerId; + // SportBukkit End +- public int protocolVersion = Protocol.LATEST; // SportBukkit + private PacketListener m; + private IChatBaseComponent n; + private boolean o; +diff --git a/src/main/java/net/minecraft/server/PacketDataSerializer.java b/src/main/java/net/minecraft/server/PacketDataSerializer.java +--- a/src/main/java/net/minecraft/server/PacketDataSerializer.java ++++ b/src/main/java/net/minecraft/server/PacketDataSerializer.java +@@ -23,10 +23,14 @@ import javax.annotation.Nullable; + + import org.bukkit.craftbukkit.inventory.CraftItemStack; // CraftBukkit + import org.bukkit.craftbukkit.protocol.Protocol; // SportBukkit ++import org.bukkit.craftbukkit.protocol.RenderableComponent; + + public class PacketDataSerializer extends ByteBuf { + +- public int protocolVersion = Protocol.LATEST; // SportBukkit ++ // SportBukkit start ++ public int protocolVersion = Protocol.LATEST; ++ public UUID playerId; ++ // SportBukkit end + + private final ByteBuf a; + +@@ -131,6 +135,12 @@ public class PacketDataSerializer extends ByteBuf { + + public PacketDataSerializer writeChatComponent(IChatBaseComponent chat) { return a(chat); } // SportBukkit - alias + public PacketDataSerializer a(IChatBaseComponent ichatbasecomponent) { ++ // SportBukkit start ++ if(ichatbasecomponent instanceof RenderableComponent) { ++ ((RenderableComponent) ichatbasecomponent).serialize(this); ++ return this; ++ } else ++ // SportBukkit end + return this.a(IChatBaseComponent.ChatSerializer.a(ichatbasecomponent)); + } + +diff --git a/src/main/java/net/minecraft/server/PacketEncoder.java b/src/main/java/net/minecraft/server/PacketEncoder.java +--- a/src/main/java/net/minecraft/server/PacketEncoder.java ++++ b/src/main/java/net/minecraft/server/PacketEncoder.java +@@ -33,7 +33,10 @@ public class PacketEncoder extends MessageToByteEncoder> { + throw new IOException("Can\'t serialize unregistered packet"); + } else { + PacketDataSerializer packetdataserializer = new PacketDataSerializer(bytebuf); +- packetdataserializer.protocolVersion = networkManager.protocolVersion; // SportBukkit - set proto ++ // SportBukkit start ++ packetdataserializer.protocolVersion = networkManager.protocolVersion; ++ packetdataserializer.playerId = networkManager.playerId; ++ // SportBukkit end + + packetdataserializer.d(integer.intValue()); + +diff --git a/src/main/java/net/minecraft/server/PacketPlayOutBoss.java b/src/main/java/net/minecraft/server/PacketPlayOutBoss.java +--- a/src/main/java/net/minecraft/server/PacketPlayOutBoss.java ++++ b/src/main/java/net/minecraft/server/PacketPlayOutBoss.java +@@ -3,7 +3,18 @@ package net.minecraft.server; + import java.io.IOException; + import java.util.UUID; + +-public class PacketPlayOutBoss implements Packet { ++// SportBukkit start ++import org.bukkit.craftbukkit.protocol.RenderableComponent; ++import org.bukkit.craftbukkit.protocol.Renderable; ++import org.bukkit.entity.Player; ++ ++public class PacketPlayOutBoss implements Packet, Renderable { ++ ++ @Override ++ public void render(Player viewer) { ++ RenderableComponent.render(c, viewer); ++ } ++ // SportBukkit end + + private UUID a; + private PacketPlayOutBoss.Action b; +@@ -20,7 +31,7 @@ public class PacketPlayOutBoss implements Packet { + public PacketPlayOutBoss(PacketPlayOutBoss.Action packetplayoutboss_action, BossBattle bossbattle) { + this.b = packetplayoutboss_action; + this.a = bossbattle.d(); +- this.c = bossbattle.e(); ++ this.c = RenderableComponent.wrap(bossbattle.e()); // SportBukkit + this.d = bossbattle.getProgress(); + this.e = bossbattle.g(); + this.f = bossbattle.h(); +diff --git a/src/main/java/net/minecraft/server/PacketPlayOutChat.java b/src/main/java/net/minecraft/server/PacketPlayOutChat.java +--- a/src/main/java/net/minecraft/server/PacketPlayOutChat.java ++++ b/src/main/java/net/minecraft/server/PacketPlayOutChat.java +@@ -2,10 +2,46 @@ package net.minecraft.server; + + import java.io.IOException; + +-public class PacketPlayOutChat implements Packet { ++// SportBukkit start ++import net.md_5.bungee.api.ChatMessageType; ++import net.md_5.bungee.api.chat.BaseComponent; ++import net.md_5.bungee.api.chat.TextComponent; ++import org.bukkit.craftbukkit.protocol.RenderableComponent; ++import org.bukkit.craftbukkit.protocol.Renderable; ++import org.bukkit.craftbukkit.util.BungeeChatUtils; ++import org.bukkit.entity.Player; ++ ++public class PacketPlayOutChat implements Packet, Renderable { ++ ++ private class RenderableChatComponent extends RenderableComponent { ++ ++ public RenderableChatComponent(BaseComponent bukkit) { ++ super(bukkit); ++ } ++ ++ @Override ++ protected BaseComponent afterRender(BaseComponent text) { ++ if(b == ChatMessageType.ACTION_BAR.ordinal()) { ++ // Work around a client bug where component text above the hotbar is not formatted. ++ // The only way to format it is by wrapping legacy formatting in a text component. ++ text = new TextComponent(TextComponent.toLegacyText(text)); ++ } ++ return text; ++ } ++ } ++ ++ @Override ++ public void render(Player viewer) { ++ ((RenderableComponent) a).render(viewer); ++ } ++ ++ public PacketPlayOutChat(BaseComponent text, ChatMessageType position) { ++ this.a = new RenderableChatComponent(text); ++ this.b = (byte) position.ordinal(); ++ } ++ // SportBukkit end + + private IChatBaseComponent a; +- public net.md_5.bungee.api.chat.BaseComponent[] components; // Spigot + private byte b; + + public PacketPlayOutChat() {} +@@ -15,7 +51,7 @@ public class PacketPlayOutChat implements Packet { + } + + public PacketPlayOutChat(IChatBaseComponent ichatbasecomponent, byte b0) { +- this.a = ichatbasecomponent; ++ this.a = new RenderableChatComponent(BungeeChatUtils.toBungee(ichatbasecomponent)); // SportBukkit + this.b = b0; + } + +@@ -25,13 +61,7 @@ public class PacketPlayOutChat implements Packet { + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { +- // Spigot start +- if (components != null) { +- packetdataserializer.a(net.md_5.bungee.chat.ComponentSerializer.toString(components)); +- } else { +- packetdataserializer.a(this.a); +- } +- // Spigot end ++ packetdataserializer.a(this.a); + packetdataserializer.writeByte(this.b); + } + +diff --git a/src/main/java/net/minecraft/server/PacketPlayOutCombatEvent.java b/src/main/java/net/minecraft/server/PacketPlayOutCombatEvent.java +--- a/src/main/java/net/minecraft/server/PacketPlayOutCombatEvent.java ++++ b/src/main/java/net/minecraft/server/PacketPlayOutCombatEvent.java +@@ -2,7 +2,20 @@ package net.minecraft.server; + + import java.io.IOException; + +-public class PacketPlayOutCombatEvent implements Packet { ++// SportBukkit start ++import org.bukkit.craftbukkit.protocol.RenderableComponent; ++import org.bukkit.craftbukkit.protocol.Renderable; ++import org.bukkit.entity.Player; ++ ++public class PacketPlayOutCombatEvent implements Packet, Renderable { ++ ++ @Override ++ public void render(Player viewer) { ++ if(a == EnumCombatEventType.ENTITY_DIED) { ++ RenderableComponent.render(e, viewer); ++ } ++ } ++ // SportBukkit end + + public PacketPlayOutCombatEvent.EnumCombatEventType a; + public int b; +@@ -30,7 +43,7 @@ public class PacketPlayOutCombatEvent implements Packet { + this.b = combattracker.h().getId(); + this.c = entityliving == null ? -1 : entityliving.getId(); + if (flag) { +- this.e = combattracker.getDeathMessage(); ++ this.e = RenderableComponent.wrap(combattracker.getDeathMessage()); // SportBukkit + } else { + this.e = new ChatComponentText(""); + } +diff --git a/src/main/java/net/minecraft/server/PacketPlayOutPlayerInfo.java b/src/main/java/net/minecraft/server/PacketPlayOutPlayerInfo.java +--- a/src/main/java/net/minecraft/server/PacketPlayOutPlayerInfo.java ++++ b/src/main/java/net/minecraft/server/PacketPlayOutPlayerInfo.java +@@ -9,12 +9,24 @@ import java.util.Iterator; + import java.util.List; + import javax.annotation.Nullable; + +-public class PacketPlayOutPlayerInfo implements Packet { +- +- private PacketPlayOutPlayerInfo.EnumPlayerInfoAction a; +- private final List b = Lists.newArrayList(); ++// SportBukkit start ++import java.util.UUID; ++ ++import net.md_5.bungee.api.chat.BaseComponent; ++import org.bukkit.GameMode; ++import org.bukkit.Skin; ++import org.bukkit.craftbukkit.protocol.RenderableComponent; ++import org.bukkit.craftbukkit.protocol.Renderable; ++import org.bukkit.craftbukkit.util.Skins; ++import org.bukkit.entity.Player; ++ ++public class PacketPlayOutPlayerInfo implements Packet, Renderable { ++ ++ @Override ++ public void render(Player viewer) { ++ b.forEach(info -> RenderableComponent.render(info.e, viewer)); ++ } + +- // SportBukkit start - add constructor + public PacketPlayOutPlayerInfo(EnumPlayerInfoAction a) { + this.a = a; + } +@@ -28,6 +40,9 @@ public class PacketPlayOutPlayerInfo implements Packet { + } + // SportBukkit end + ++ private PacketPlayOutPlayerInfo.EnumPlayerInfoAction a; ++ private final List b = Lists.newArrayList(); ++ + public PacketPlayOutPlayerInfo() {} + + public PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction packetplayoutplayerinfo_enumplayerinfoaction, EntityPlayer... aentityplayer) { +@@ -144,15 +159,6 @@ public class PacketPlayOutPlayerInfo implements Packet { + + packetdataserializer.d(packetplayoutplayerinfo_playerinfodata.c().getId()); + packetdataserializer.d(packetplayoutplayerinfo_playerinfodata.b()); +- +- // SportBukkit start +- if (packetplayoutplayerinfo_playerinfodata.displayName != null) { +- packetdataserializer.writeBoolean(true); +- packetdataserializer.a(net.md_5.bungee.chat.ComponentSerializer.toString(packetplayoutplayerinfo_playerinfodata.displayName)); +- break; +- } +- // SportBukkit end +- + if (packetplayoutplayerinfo_playerinfodata.d() == null) { + packetdataserializer.writeBoolean(false); + } else { +@@ -173,15 +179,6 @@ public class PacketPlayOutPlayerInfo implements Packet { + + case UPDATE_DISPLAY_NAME: + packetdataserializer.a(packetplayoutplayerinfo_playerinfodata.a().getId()); +- +- // SportBukkit start +- if (packetplayoutplayerinfo_playerinfodata.displayName != null) { +- packetdataserializer.writeBoolean(true); +- packetdataserializer.a(net.md_5.bungee.chat.ComponentSerializer.toString(packetplayoutplayerinfo_playerinfodata.displayName)); +- break; +- } +- // SportBukkit end +- + if (packetplayoutplayerinfo_playerinfodata.d() == null) { + packetdataserializer.writeBoolean(false); + } else { +@@ -207,7 +204,20 @@ public class PacketPlayOutPlayerInfo implements Packet { + + public class PlayerInfoData { + +- public net.md_5.bungee.api.chat.BaseComponent[] displayName; // SportBukkit ++ // SportBukkit start ++ public PlayerInfoData(UUID uuid, String name, @Nullable BaseComponent displayName, GameMode gamemode, int ping, @Nullable Skin skin) { ++ this(new GameProfile(uuid, name), ++ ping, ++ EnumGamemode.getById(gamemode.getValue()), ++ displayName == null ? null : new RenderableComponent(displayName)); ++ ++ if(skin != null) { ++ Skins.toProperties(skin).asMap().forEach( ++ (k, v) -> d.getProperties().putAll(k, v) ++ ); ++ } ++ } ++ // SportBukkit end + + private final int b; + private final EnumGamemode c; +@@ -218,7 +228,7 @@ public class PacketPlayOutPlayerInfo implements Packet { + this.d = gameprofile; + this.b = i; + this.c = enumgamemode; +- this.e = ichatbasecomponent; ++ this.e = RenderableComponent.wrap(ichatbasecomponent); // SportBukkit + } + + public GameProfile a() { +diff --git a/src/main/java/net/minecraft/server/PacketPlayOutPlayerListHeaderFooter.java b/src/main/java/net/minecraft/server/PacketPlayOutPlayerListHeaderFooter.java +--- a/src/main/java/net/minecraft/server/PacketPlayOutPlayerListHeaderFooter.java ++++ b/src/main/java/net/minecraft/server/PacketPlayOutPlayerListHeaderFooter.java +@@ -2,9 +2,25 @@ package net.minecraft.server; + + import java.io.IOException; + +-public class PacketPlayOutPlayerListHeaderFooter implements Packet { ++// SportBukkit start ++import net.md_5.bungee.api.chat.BaseComponent; ++import org.bukkit.craftbukkit.protocol.RenderableComponent; ++import org.bukkit.craftbukkit.protocol.Renderable; ++import org.bukkit.entity.Player; ++ ++public class PacketPlayOutPlayerListHeaderFooter implements Packet, Renderable { ++ ++ @Override ++ public void render(Player viewer) { ++ RenderableComponent.render(a, viewer); ++ RenderableComponent.render(b, viewer); ++ } + +- public net.md_5.bungee.api.chat.BaseComponent[] header, footer; // SportBukkit ++ public PacketPlayOutPlayerListHeaderFooter(BaseComponent header, BaseComponent footer) { ++ a = new RenderableComponent(header); ++ b = new RenderableComponent(footer); ++ } ++ // SportBukkit end + + private IChatBaseComponent a; + private IChatBaseComponent b; +@@ -21,19 +37,8 @@ public class PacketPlayOutPlayerListHeaderFooter implements Packet { ++// SportBukkit start ++import net.md_5.bungee.api.chat.BaseComponent; ++import org.bukkit.craftbukkit.protocol.RenderableComponent; ++import org.bukkit.craftbukkit.protocol.Renderable; ++import org.bukkit.entity.Player; ++ ++public class PacketPlayOutTitle implements Packet, Renderable { ++ ++ @Override ++ public void render(Player viewer) { ++ RenderableComponent.render(b, viewer); ++ } ++ ++ public PacketPlayOutTitle(EnumTitleAction action, @Nullable BaseComponent text, int fadeIn, int stay, int fadeOut) { ++ this(action, text == null ? null : new RenderableComponent(text), fadeIn, stay, fadeOut); ++ } ++ // SportBukkit end + + private PacketPlayOutTitle.EnumTitleAction a; + private IChatBaseComponent b; +@@ -12,18 +28,6 @@ public class PacketPlayOutTitle implements Packet { + private int d; + private int e; + +- // SportBukkit start +- public net.md_5.bungee.api.chat.BaseComponent[] components; +- +- public PacketPlayOutTitle(EnumTitleAction action, net.md_5.bungee.api.chat.BaseComponent[] components, int fadeIn, int stay, int fadeOut) { +- this.a = action; +- this.components = components; +- this.c = fadeIn; +- this.d = stay; +- this.e = fadeOut; +- } +- // SportBukkit end +- + public PacketPlayOutTitle() {} + + public PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction packetplayouttitle_enumtitleaction, IChatBaseComponent ichatbasecomponent) { +@@ -36,7 +40,7 @@ public class PacketPlayOutTitle implements Packet { + + public PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction packetplayouttitle_enumtitleaction, @Nullable IChatBaseComponent ichatbasecomponent, int i, int j, int k) { + this.a = packetplayouttitle_enumtitleaction; +- this.b = ichatbasecomponent; ++ this.b = RenderableComponent.wrap(ichatbasecomponent); // SportBukkit + this.c = i; + this.d = j; + this.e = k; +@@ -66,13 +70,7 @@ public class PacketPlayOutTitle implements Packet { + } + // SportBukkit end + if (this.a == PacketPlayOutTitle.EnumTitleAction.TITLE || this.a == PacketPlayOutTitle.EnumTitleAction.SUBTITLE || this.a == PacketPlayOutTitle.EnumTitleAction.ACTIONBAR) { +- // SportBukkit start +- if(this.components != null) { +- packetdataserializer.a(net.md_5.bungee.chat.ComponentSerializer.toString(components)); +- } else { +- packetdataserializer.a(this.b); +- } +- // SportBukkit end ++ packetdataserializer.a(this.b); + } + + if (this.a == PacketPlayOutTitle.EnumTitleAction.TIMES) { +diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java +--- a/src/main/java/net/minecraft/server/PlayerConnection.java ++++ b/src/main/java/net/minecraft/server/PlayerConnection.java +@@ -3,7 +3,6 @@ package net.minecraft.server; + import com.google.common.collect.Lists; + import com.google.common.primitives.Doubles; + import com.google.common.primitives.Floats; +-import com.google.common.util.concurrent.Futures; + import io.netty.util.concurrent.Future; + import io.netty.util.concurrent.GenericFutureListener; + import java.io.IOException; +@@ -31,6 +30,7 @@ import org.bukkit.craftbukkit.entity.CraftPlayer; + import org.bukkit.craftbukkit.event.CraftEventFactory; + import org.bukkit.craftbukkit.inventory.CraftInventoryView; + import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.craftbukkit.protocol.Renderable; + import org.bukkit.craftbukkit.util.BungeeChatUtils; + import org.bukkit.craftbukkit.util.CraftChatMessage; + import org.bukkit.craftbukkit.util.LazyPlayerSet; +@@ -1260,6 +1260,10 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable { + } + } + } ++ ++ } ++ if(packet instanceof Renderable) { ++ ((Renderable) packet).render(getPlayer()); + } + // CraftBukkit end + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -24,6 +24,7 @@ import java.util.logging.Logger; + import java.util.regex.Pattern; + import javax.annotation.Nullable; + import javax.imageio.ImageIO; ++import javax.inject.Provider; + + import com.google.common.collect.Maps; + import com.google.inject.Guice; +@@ -145,6 +146,8 @@ import org.bukkit.event.server.TabCompleteEvent; + import net.md_5.bungee.api.chat.BaseComponent; + import tc.oc.minecraft.api.configuration.InvalidConfigurationException; + import tc.oc.minecraft.api.plugin.PluginFinder; ++import tc.oc.minecraft.api.text.TextRenderContext; ++import tc.oc.minecraft.api.text.TextRenderer; + + public final class CraftServer extends CraftBukkitRuntime implements Server { + private static final Player[] EMPTY_PLAYER_ARRAY = new Player[0]; +@@ -2000,4 +2003,23 @@ public final class CraftServer extends CraftBukkitRuntime implements Server { + } + } + } ++ ++ @Override ++ public TextRenderContext textRenderContext() { ++ return textRenderContext; ++ } ++ ++ private final TextRenderContext textRenderContext = new TextRenderContext() { ++ @Override ++ public BaseComponent render(BaseComponent text, tc.oc.minecraft.api.command.CommandSender viewer) { ++ for(Plugin plugin : getPluginManager().getPlugins()) { ++ if(plugin.isEnabled()) { ++ for(Provider renderer : plugin.textRenderers()) { ++ text = renderer.get().render(this, text, viewer); ++ } ++ } ++ } ++ return text; ++ } ++ }; + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -1898,76 +1898,60 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + } + + @Override +- public void sendMessage(net.md_5.bungee.api.ChatMessageType position, BaseComponent... message) { ++ public void sendMessage(net.md_5.bungee.api.ChatMessageType position, BaseComponent message) { + if ( getHandle().playerConnection == null ) return; +- +- PacketPlayOutChat packet = new PacketPlayOutChat(null, (byte) position.ordinal()); +- if(position == ChatMessageType.ACTION_BAR) { +- // Work around a client bug where component text above the hotbar is not formatted. +- // The only way to format it is by wrapping legacy formatting in a text component. +- packet.components = new BaseComponent[]{ new TextComponent(TextComponent.toLegacyText(message)) }; +- } else { +- packet.components = message; +- } +- getHandle().playerConnection.sendPacket(packet); ++ getHandle().playerConnection.sendPacket(new PacketPlayOutChat(message, position)); + } + + @Override +- public void sendMessage(net.md_5.bungee.api.ChatMessageType position, BaseComponent message) { +- sendMessage(position, new BaseComponent[] {message}); ++ public void sendMessage(net.md_5.bungee.api.ChatMessageType position, BaseComponent... messages) { ++ sendMessage(position, new TextComponent(messages)); + } + + @Override + public void sendMessage(BaseComponent component) { +- sendMessage( new BaseComponent[] { component } ); ++ sendMessage(ChatMessageType.CHAT, component); + } + + @Override + public void sendMessage(BaseComponent... components) { +- if ( getHandle().playerConnection == null ) return; +- +- PacketPlayOutChat packet = new PacketPlayOutChat(); +- packet.components = components; +- getHandle().playerConnection.sendPacket(packet); ++ sendMessage(ChatMessageType.CHAT, components); + } + + @Override +- public void setPlayerListHeaderFooter(BaseComponent[] header, BaseComponent[] footer) { +- PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter(); +- packet.header = header; +- packet.footer = footer; +- getHandle().playerConnection.sendPacket(packet); ++ public void setPlayerListHeaderFooter(BaseComponent header, BaseComponent footer) { ++ getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerListHeaderFooter(header, footer)); + } + + @Override +- public void setPlayerListHeaderFooter(BaseComponent header, BaseComponent footer) { +- this.setPlayerListHeaderFooter(header == null ? null : new BaseComponent[]{ header }, +- footer == null ? null : new BaseComponent[]{ footer }); ++ public void setPlayerListHeaderFooter(BaseComponent[] header, BaseComponent[] footer) { ++ this.setPlayerListHeaderFooter(header == null ? null : new TextComponent(header), ++ footer == null ? null : new TextComponent(footer)); + } + + @Override + public void setTitleTimes(int fadeInTicks, int stayTicks, int fadeOutTicks) { +- getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.TIMES, (BaseComponent[]) null, fadeInTicks, stayTicks, fadeOutTicks)); ++ getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.TIMES, (BaseComponent) null, fadeInTicks, stayTicks, fadeOutTicks)); + } + + @Override + public void setSubtitle(BaseComponent[] subtitle) { +- getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.SUBTITLE, subtitle, 0, 0, 0)); ++ setSubtitle(new TextComponent(subtitle)); + } + + @Override + public void setSubtitle(BaseComponent subtitle) { +- setSubtitle(new BaseComponent[] { subtitle }); ++ getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.SUBTITLE, subtitle, 0, 0, 0)); + } + + @Override + public void showTitle(BaseComponent[] title) { +- getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.TITLE, title, 0, 0, 0)); ++ showTitle(new TextComponent(title)); + } + + @Override + public void showTitle(BaseComponent title) { +- showTitle(new BaseComponent[] {title}); ++ getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.TITLE, title, 0, 0, 0)); + } + + @Override +@@ -1986,7 +1970,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + + @Override + public void hideTitle() { +- getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.CLEAR, (BaseComponent[]) null, 0, 0, 0)); ++ getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.CLEAR, (BaseComponent) null, 0, 0, 0)); + } + + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/protocol/Renderable.java b/src/main/java/org/bukkit/craftbukkit/protocol/Renderable.java +new file mode 100644 +index 0000000..875671a +--- /dev/null ++++ b/src/main/java/org/bukkit/craftbukkit/protocol/Renderable.java +@@ -0,0 +1,8 @@ ++package org.bukkit.craftbukkit.protocol; ++ ++import org.bukkit.entity.Player; ++ ++public interface Renderable { ++ ++ void render(Player viewer); ++} +diff --git a/src/main/java/org/bukkit/craftbukkit/protocol/RenderableComponent.java b/src/main/java/org/bukkit/craftbukkit/protocol/RenderableComponent.java +new file mode 100644 +index 0000000..3018396 +--- /dev/null ++++ b/src/main/java/org/bukkit/craftbukkit/protocol/RenderableComponent.java +@@ -0,0 +1,69 @@ ++package org.bukkit.craftbukkit.protocol; ++ ++import java.util.HashMap; ++import java.util.Map; ++import java.util.UUID; ++import javax.annotation.Nullable; ++ ++import net.md_5.bungee.api.chat.BaseComponent; ++import net.md_5.bungee.chat.ComponentSerializer; ++import net.minecraft.server.ChatBaseComponent; ++import net.minecraft.server.IChatBaseComponent; ++import net.minecraft.server.PacketDataSerializer; ++import org.bukkit.craftbukkit.util.BungeeChatUtils; ++import org.bukkit.entity.Player; ++ ++import static com.google.common.base.Preconditions.checkNotNull; ++ ++public class RenderableComponent extends ChatBaseComponent { ++ ++ private final BaseComponent original; ++ private final Map rendered = new HashMap<>(); ++ ++ public static @Nullable RenderableComponent wrap(@Nullable IChatBaseComponent nms) { ++ if(nms == null) return null; ++ if(nms instanceof RenderableComponent) { ++ return (RenderableComponent) nms; ++ } ++ return new RenderableComponent(BungeeChatUtils.toBungee(nms)); ++ } ++ ++ public static void render(@Nullable IChatBaseComponent nms, Player viewer) { ++ if(nms instanceof RenderableComponent) { ++ ((RenderableComponent) nms).render(viewer); ++ } ++ } ++ ++ public RenderableComponent(BaseComponent bukkit) { ++ this.original = checkNotNull(bukkit); ++ } ++ ++ protected BaseComponent afterRender(BaseComponent text) { ++ return text; ++ } ++ ++ public void render(Player viewer) { ++ rendered.computeIfAbsent( ++ viewer.getUniqueId(), ++ uuid -> afterRender( ++ viewer.getServer() ++ .textRenderContext() ++ .render(original, viewer) ++ ) ++ ); ++ } ++ ++ public void serialize(PacketDataSerializer data) { ++ data.a(ComponentSerializer.toString(rendered.getOrDefault(data.playerId, original))); ++ } ++ ++ @Override ++ public String getText() { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public IChatBaseComponent f() { ++ throw new UnsupportedOperationException(); ++ } ++} diff --git a/snapshot/Bukkit/src/main/java/org/bukkit/BukkitRuntime.java b/snapshot/Bukkit/src/main/java/org/bukkit/BukkitRuntime.java index ed038b86..bde51768 100644 --- a/snapshot/Bukkit/src/main/java/org/bukkit/BukkitRuntime.java +++ b/snapshot/Bukkit/src/main/java/org/bukkit/BukkitRuntime.java @@ -8,6 +8,7 @@ import org.bukkit.potion.PotionEffectRegistry; import org.bukkit.registry.Key; import org.bukkit.geometry.VectorFactory; +import tc.oc.minecraft.api.text.TextRenderContext; public interface BukkitRuntime { @@ -39,4 +40,8 @@ public interface BukkitRuntime { PotionBrewRegistry potionRegistry(); PotionEffectRegistry potionEffectRegistry(); + + default TextRenderContext textRenderContext() { + return (text, viewer) -> text; + } } diff --git a/snapshot/Bukkit/src/main/java/org/bukkit/ServerModule.java b/snapshot/Bukkit/src/main/java/org/bukkit/ServerModule.java index 373967b3..9095fe9d 100644 --- a/snapshot/Bukkit/src/main/java/org/bukkit/ServerModule.java +++ b/snapshot/Bukkit/src/main/java/org/bukkit/ServerModule.java @@ -16,8 +16,10 @@ import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scoreboard.ScoreboardManager; import tc.oc.inject.SingletonModule; +import tc.oc.minecraft.api.command.Console; import tc.oc.minecraft.api.plugin.PluginFinder; import tc.oc.minecraft.api.server.LocalServer; +import tc.oc.minecraft.api.text.TextRenderContext; /** * Bindings for things that belong to a {@link Server}. @@ -35,7 +37,7 @@ protected void configure() { bind(tc.oc.minecraft.api.server.Server.class).to(LocalServer.class); bind(LocalServer.class).to(Server.class); bind(BukkitRuntime.class).to(Server.class); - bind(tc.oc.minecraft.api.command.ConsoleCommandSender.class).to(ConsoleCommandSender.class); + bind(Console.class).to(ConsoleCommandSender.class); bind(PluginFinder.class).to(PluginManager.class); } @@ -84,6 +86,11 @@ ServicesManager servicesManager(Server server) { return server.getServicesManager(); } + @Provides + TextRenderContext textRenderContext(Server server) { + return server.textRenderContext(); + } + @Provides Collection worlds(Server server) { return server.worldsById().values(); diff --git a/snapshot/Bukkit/src/main/java/org/bukkit/command/ConsoleCommandSender.java b/snapshot/Bukkit/src/main/java/org/bukkit/command/ConsoleCommandSender.java index a460d330..08f8b287 100644 --- a/snapshot/Bukkit/src/main/java/org/bukkit/command/ConsoleCommandSender.java +++ b/snapshot/Bukkit/src/main/java/org/bukkit/command/ConsoleCommandSender.java @@ -1,6 +1,7 @@ package org.bukkit.command; import org.bukkit.conversations.Conversable; +import tc.oc.minecraft.api.command.Console; -public interface ConsoleCommandSender extends CommandSender, Conversable, tc.oc.minecraft.api.command.ConsoleCommandSender { +public interface ConsoleCommandSender extends CommandSender, Conversable, Console { } diff --git a/snapshot/Bukkit/src/main/java/org/bukkit/plugin/Plugin.java b/snapshot/Bukkit/src/main/java/org/bukkit/plugin/Plugin.java index 9a1c231c..3cbdedc1 100644 --- a/snapshot/Bukkit/src/main/java/org/bukkit/plugin/Plugin.java +++ b/snapshot/Bukkit/src/main/java/org/bukkit/plugin/Plugin.java @@ -2,7 +2,10 @@ import java.io.File; import java.io.InputStream; +import java.util.Collection; +import java.util.Collections; import java.util.logging.Logger; +import javax.inject.Provider; import org.bukkit.Server; import org.bukkit.command.TabExecutor; @@ -11,6 +14,7 @@ import org.bukkit.generator.ChunkGenerator; import com.avaje.ebean.EbeanServer; +import tc.oc.minecraft.api.text.TextRenderer; /** * Represents a Plugin @@ -98,6 +102,10 @@ public interface Plugin extends TabExecutor, tc.oc.minecraft.api.plugin.Plugin, */ EventRegistry eventRegistry(); + default Collection> textRenderers() { + return Collections.emptyList(); + } + /** * Returns the Server instance currently running this plugin * diff --git a/snapshot/Bukkit/src/main/java/org/bukkit/plugin/PluginBase.java b/snapshot/Bukkit/src/main/java/org/bukkit/plugin/PluginBase.java index 44f438bf..9b702674 100644 --- a/snapshot/Bukkit/src/main/java/org/bukkit/plugin/PluginBase.java +++ b/snapshot/Bukkit/src/main/java/org/bukkit/plugin/PluginBase.java @@ -1,5 +1,6 @@ package org.bukkit.plugin; +import java.util.Collection; import java.util.Set; import javax.inject.Inject; import javax.inject.Provider; @@ -9,6 +10,7 @@ import org.bukkit.permissions.Permission; import tc.oc.minecraft.api.event.ListenerContext; import tc.oc.exception.ExceptionHandler; +import tc.oc.minecraft.api.text.TextRenderer; /** * Represents a base {@link Plugin} @@ -25,6 +27,7 @@ public abstract class PluginBase implements Plugin { @Inject private Set permissions; @Inject private Provider listenerContext; + @Inject private Provider>> textRenderers; protected void assertInjected() { if(injector == null) { @@ -50,6 +53,12 @@ public ExceptionHandler exceptionHandler() { return exceptionHandler; } + @Override + public Collection> textRenderers() { + assertInjected(); + return textRenderers.get(); + } + protected final void preEnable() { permissions.forEach(pluginManager::addPermission); listenerContext.get().enable(); diff --git a/snapshot/Bukkit/src/main/java/org/bukkit/plugin/PluginModule.java b/snapshot/Bukkit/src/main/java/org/bukkit/plugin/PluginModule.java index 1e2dda48..20f47bb7 100644 --- a/snapshot/Bukkit/src/main/java/org/bukkit/plugin/PluginModule.java +++ b/snapshot/Bukkit/src/main/java/org/bukkit/plugin/PluginModule.java @@ -11,6 +11,7 @@ import tc.oc.exception.ExceptionHandler; import tc.oc.minecraft.api.plugin.PluginDescription; import tc.oc.minecraft.api.scheduler.Scheduler; +import tc.oc.minecraft.api.text.TextRendererBinder; /** * Bindings for things belonging to a particular {@link Plugin}. @@ -26,6 +27,7 @@ protected void configure() { // Ensure these collections have bindings new PermissionBinder(binder()); new ListenerBinder(binder()); + new TextRendererBinder(binder()); bind(tc.oc.minecraft.api.plugin.Plugin.class).to(Plugin.class); bind(PluginDescription.class).to(PluginDescriptionFile.class); diff --git a/snapshot/CraftBukkit/nms-patches/LoginListener.patch b/snapshot/CraftBukkit/nms-patches/LoginListener.patch index a4348ed5..601c8ccb 100644 --- a/snapshot/CraftBukkit/nms-patches/LoginListener.patch +++ b/snapshot/CraftBukkit/nms-patches/LoginListener.patch @@ -129,7 +129,7 @@ } @Nullable -@@ -185,8 +213,77 @@ +@@ -185,8 +213,78 @@ } } @@ -141,6 +141,7 @@ + String playerName = i.getName(); + java.net.InetAddress address = ((java.net.InetSocketAddress) networkManager.getSocketAddress()).getAddress(); + java.util.UUID uniqueId = i.getId(); ++ networkManager.playerId = uniqueId; + final org.bukkit.craftbukkit.CraftServer server = LoginListener.this.server.server; + AsyncPlayerPreLoginEvent asyncEvent = new AsyncPlayerPreLoginEvent(playerName, address, uniqueId); + diff --git a/snapshot/CraftBukkit/nms-patches/NetworkManager.patch b/snapshot/CraftBukkit/nms-patches/NetworkManager.patch index e77993be..b3f4da7a 100644 --- a/snapshot/CraftBukkit/nms-patches/NetworkManager.patch +++ b/snapshot/CraftBukkit/nms-patches/NetworkManager.patch @@ -9,7 +9,7 @@ public class NetworkManager extends SimpleChannelInboundHandler> { private static final Logger g = LogManager.getLogger(); -@@ -65,7 +67,12 @@ +@@ -65,7 +67,14 @@ private final Queue i = Queues.newConcurrentLinkedQueue(); private final ReentrantReadWriteLock j = new ReentrantReadWriteLock(); public Channel channel; @@ -18,12 +18,14 @@ + public SocketAddress l; + public java.util.UUID spoofedUUID; + public com.mojang.authlib.properties.Property[] spoofedProfile; ++ ++ public int protocolVersion = Protocol.LATEST; ++ public java.util.UUID playerId; + // SportBukkit End -+ public int protocolVersion = Protocol.LATEST; // SportBukkit private PacketListener m; private IChatBaseComponent n; private boolean o; -@@ -83,7 +90,7 @@ +@@ -83,7 +92,7 @@ try { this.setProtocol(EnumProtocol.HANDSHAKING); } catch (Throwable throwable) { @@ -32,7 +34,7 @@ } } -@@ -103,18 +110,19 @@ +@@ -103,18 +112,19 @@ if (throwable instanceof TimeoutException) { chatmessage = new ChatMessage("disconnect.timeout", new Object[0]); @@ -54,7 +56,7 @@ } catch (CancelledPacketHandleException cancelledpackethandleexception) { ; } -@@ -233,7 +241,7 @@ +@@ -233,7 +243,7 @@ public void close(IChatBaseComponent ichatbasecomponent) { if (this.channel.isOpen()) { @@ -63,7 +65,7 @@ this.n = ichatbasecomponent; } -@@ -257,6 +265,7 @@ +@@ -257,6 +267,7 @@ return this.channel == null; } @@ -71,7 +73,7 @@ public PacketListener i() { return this.m; } -@@ -310,7 +319,7 @@ +@@ -310,7 +321,7 @@ } } diff --git a/snapshot/CraftBukkit/nms-patches/PacketDataSerializer.patch b/snapshot/CraftBukkit/nms-patches/PacketDataSerializer.patch index 4f88c84b..cb78f289 100644 --- a/snapshot/CraftBukkit/nms-patches/PacketDataSerializer.patch +++ b/snapshot/CraftBukkit/nms-patches/PacketDataSerializer.patch @@ -1,20 +1,24 @@ --- a/net/minecraft/server/PacketDataSerializer.java +++ b/net/minecraft/server/PacketDataSerializer.java -@@ -21,8 +21,13 @@ +@@ -21,8 +21,17 @@ import java.util.UUID; import javax.annotation.Nullable; +import org.bukkit.craftbukkit.inventory.CraftItemStack; // CraftBukkit +import org.bukkit.craftbukkit.protocol.Protocol; // SportBukkit ++import org.bukkit.craftbukkit.protocol.RenderableComponent; + public class PacketDataSerializer extends ByteBuf { -+ public int protocolVersion = Protocol.LATEST; // SportBukkit ++ // SportBukkit start ++ public int protocolVersion = Protocol.LATEST; ++ public UUID playerId; ++ // SportBukkit end + private final ByteBuf a; public PacketDataSerializer(ByteBuf bytebuf) { -@@ -114,6 +119,7 @@ +@@ -114,6 +123,7 @@ return BlockPosition.fromLong(this.readLong()); } @@ -22,12 +26,18 @@ public PacketDataSerializer a(BlockPosition blockposition) { this.writeLong(blockposition.asLong()); return this; -@@ -123,18 +129,21 @@ +@@ -123,18 +133,27 @@ return IChatBaseComponent.ChatSerializer.a(this.e(32767)); } + public PacketDataSerializer writeChatComponent(IChatBaseComponent chat) { return a(chat); } // SportBukkit - alias public PacketDataSerializer a(IChatBaseComponent ichatbasecomponent) { ++ // SportBukkit start ++ if(ichatbasecomponent instanceof RenderableComponent) { ++ ((RenderableComponent) ichatbasecomponent).serialize(this); ++ return this; ++ } else ++ // SportBukkit end return this.a(IChatBaseComponent.ChatSerializer.a(ichatbasecomponent)); } @@ -45,7 +55,7 @@ public int g() { int i = 0; int j = 0; -@@ -179,6 +188,7 @@ +@@ -179,6 +198,7 @@ return new UUID(this.readLong(), this.readLong()); } @@ -53,7 +63,7 @@ public PacketDataSerializer d(int i) { while ((i & -128) != 0) { this.writeByte(i & 127 | 128); -@@ -205,7 +215,7 @@ +@@ -205,7 +225,7 @@ } else { try { NBTCompressedStreamTools.a(nbttagcompound, (DataOutput) (new ByteBufOutputStream(this))); @@ -62,21 +72,21 @@ throw new EncoderException(ioexception); } } -@@ -232,8 +242,12 @@ +@@ -232,8 +252,12 @@ } public PacketDataSerializer a(ItemStack itemstack) { - if (itemstack.isEmpty()) { + if (itemstack.isEmpty() || itemstack.getItem() == null) { // CraftBukkit - NPE fix itemstack.getItem() - this.writeShort(-1); ++ this.writeShort(-1); + // SportBukkit start - legacy clients will crash if they get an unknown item + } else if(protocolVersion < itemstack.getItem().proto()) { -+ this.writeShort(-1); + this.writeShort(-1); + // SportBukkit end } else { this.writeShort(Item.getId(itemstack.getItem())); this.writeByte(itemstack.getCount()); -@@ -261,6 +275,11 @@ +@@ -261,6 +285,11 @@ ItemStack itemstack = new ItemStack(Item.getById(short0), b0, short1); itemstack.setTag(this.j()); diff --git a/snapshot/CraftBukkit/nms-patches/PacketEncoder.patch b/snapshot/CraftBukkit/nms-patches/PacketEncoder.patch index b2817d94..1ac17795 100644 --- a/snapshot/CraftBukkit/nms-patches/PacketEncoder.patch +++ b/snapshot/CraftBukkit/nms-patches/PacketEncoder.patch @@ -12,11 +12,14 @@ if (PacketEncoder.a.isDebugEnabled()) { PacketEncoder.a.debug(PacketEncoder.b, "OUT: [{}:{}] {}", new Object[] { channelhandlercontext.channel().attr(NetworkManager.c).get(), integer, packet.getClass().getName()}); -@@ -30,19 +33,20 @@ +@@ -30,19 +33,23 @@ throw new IOException("Can\'t serialize unregistered packet"); } else { PacketDataSerializer packetdataserializer = new PacketDataSerializer(bytebuf); -+ packetdataserializer.protocolVersion = networkManager.protocolVersion; // SportBukkit - set proto ++ // SportBukkit start ++ packetdataserializer.protocolVersion = networkManager.protocolVersion; ++ packetdataserializer.playerId = networkManager.playerId; ++ // SportBukkit end packetdataserializer.d(integer.intValue()); diff --git a/snapshot/CraftBukkit/nms-patches/PacketPlayOutBoss.patch b/snapshot/CraftBukkit/nms-patches/PacketPlayOutBoss.patch new file mode 100644 index 00000000..530a87f5 --- /dev/null +++ b/snapshot/CraftBukkit/nms-patches/PacketPlayOutBoss.patch @@ -0,0 +1,31 @@ +--- a/net/minecraft/server/PacketPlayOutBoss.java ++++ b/net/minecraft/server/PacketPlayOutBoss.java +@@ -3,7 +3,18 @@ + import java.io.IOException; + import java.util.UUID; + +-public class PacketPlayOutBoss implements Packet { ++// SportBukkit start ++import org.bukkit.craftbukkit.protocol.RenderableComponent; ++import org.bukkit.craftbukkit.protocol.Renderable; ++import org.bukkit.entity.Player; ++ ++public class PacketPlayOutBoss implements Packet, Renderable { ++ ++ @Override ++ public void render(Player viewer) { ++ RenderableComponent.render(c, viewer); ++ } ++ // SportBukkit end + + private UUID a; + private PacketPlayOutBoss.Action b; +@@ -20,7 +31,7 @@ + public PacketPlayOutBoss(PacketPlayOutBoss.Action packetplayoutboss_action, BossBattle bossbattle) { + this.b = packetplayoutboss_action; + this.a = bossbattle.d(); +- this.c = bossbattle.e(); ++ this.c = RenderableComponent.wrap(bossbattle.e()); // SportBukkit + this.d = bossbattle.getProgress(); + this.e = bossbattle.g(); + this.f = bossbattle.h(); diff --git a/snapshot/CraftBukkit/nms-patches/PacketPlayOutChat.patch b/snapshot/CraftBukkit/nms-patches/PacketPlayOutChat.patch index fd35a4c6..47202ee2 100644 --- a/snapshot/CraftBukkit/nms-patches/PacketPlayOutChat.patch +++ b/snapshot/CraftBukkit/nms-patches/PacketPlayOutChat.patch @@ -1,25 +1,57 @@ --- a/net/minecraft/server/PacketPlayOutChat.java +++ b/net/minecraft/server/PacketPlayOutChat.java -@@ -5,6 +5,7 @@ - public class PacketPlayOutChat implements Packet { +@@ -2,7 +2,44 @@ + + import java.io.IOException; + +-public class PacketPlayOutChat implements Packet { ++// SportBukkit start ++import net.md_5.bungee.api.ChatMessageType; ++import net.md_5.bungee.api.chat.BaseComponent; ++import net.md_5.bungee.api.chat.TextComponent; ++import org.bukkit.craftbukkit.protocol.RenderableComponent; ++import org.bukkit.craftbukkit.protocol.Renderable; ++import org.bukkit.craftbukkit.util.BungeeChatUtils; ++import org.bukkit.entity.Player; ++ ++public class PacketPlayOutChat implements Packet, Renderable { ++ ++ private class RenderableChatComponent extends RenderableComponent { ++ ++ public RenderableChatComponent(BaseComponent bukkit) { ++ super(bukkit); ++ } ++ ++ @Override ++ protected BaseComponent afterRender(BaseComponent text) { ++ if(b == ChatMessageType.ACTION_BAR.ordinal()) { ++ // Work around a client bug where component text above the hotbar is not formatted. ++ // The only way to format it is by wrapping legacy formatting in a text component. ++ text = new TextComponent(TextComponent.toLegacyText(text)); ++ } ++ return text; ++ } ++ } ++ ++ @Override ++ public void render(Player viewer) { ++ ((RenderableComponent) a).render(viewer); ++ } ++ ++ public PacketPlayOutChat(BaseComponent text, ChatMessageType position) { ++ this.a = new RenderableChatComponent(text); ++ this.b = (byte) position.ordinal(); ++ } ++ // SportBukkit end private IChatBaseComponent a; -+ public net.md_5.bungee.api.chat.BaseComponent[] components; // Spigot private byte b; - - public PacketPlayOutChat() {} -@@ -24,7 +25,13 @@ +@@ -14,7 +51,7 @@ } - public void b(PacketDataSerializer packetdataserializer) throws IOException { -- packetdataserializer.a(this.a); -+ // Spigot start -+ if (components != null) { -+ packetdataserializer.a(net.md_5.bungee.chat.ComponentSerializer.toString(components)); -+ } else { -+ packetdataserializer.a(this.a); -+ } -+ // Spigot end - packetdataserializer.writeByte(this.b); + public PacketPlayOutChat(IChatBaseComponent ichatbasecomponent, byte b0) { +- this.a = ichatbasecomponent; ++ this.a = new RenderableChatComponent(BungeeChatUtils.toBungee(ichatbasecomponent)); // SportBukkit + this.b = b0; } diff --git a/snapshot/CraftBukkit/nms-patches/PacketPlayOutCombatEvent.patch b/snapshot/CraftBukkit/nms-patches/PacketPlayOutCombatEvent.patch new file mode 100644 index 00000000..2efc1419 --- /dev/null +++ b/snapshot/CraftBukkit/nms-patches/PacketPlayOutCombatEvent.patch @@ -0,0 +1,33 @@ +--- a/net/minecraft/server/PacketPlayOutCombatEvent.java ++++ b/net/minecraft/server/PacketPlayOutCombatEvent.java +@@ -2,7 +2,20 @@ + + import java.io.IOException; + +-public class PacketPlayOutCombatEvent implements Packet { ++// SportBukkit start ++import org.bukkit.craftbukkit.protocol.RenderableComponent; ++import org.bukkit.craftbukkit.protocol.Renderable; ++import org.bukkit.entity.Player; ++ ++public class PacketPlayOutCombatEvent implements Packet, Renderable { ++ ++ @Override ++ public void render(Player viewer) { ++ if(a == EnumCombatEventType.ENTITY_DIED) { ++ RenderableComponent.render(e, viewer); ++ } ++ } ++ // SportBukkit end + + public PacketPlayOutCombatEvent.EnumCombatEventType a; + public int b; +@@ -30,7 +43,7 @@ + this.b = combattracker.h().getId(); + this.c = entityliving == null ? -1 : entityliving.getId(); + if (flag) { +- this.e = combattracker.getDeathMessage(); ++ this.e = RenderableComponent.wrap(combattracker.getDeathMessage()); // SportBukkit + } else { + this.e = new ChatComponentText(""); + } diff --git a/snapshot/CraftBukkit/nms-patches/PacketPlayOutPlayerInfo.patch b/snapshot/CraftBukkit/nms-patches/PacketPlayOutPlayerInfo.patch index 6ecbdada..78aea251 100644 --- a/snapshot/CraftBukkit/nms-patches/PacketPlayOutPlayerInfo.patch +++ b/snapshot/CraftBukkit/nms-patches/PacketPlayOutPlayerInfo.patch @@ -1,10 +1,28 @@ --- a/net/minecraft/server/PacketPlayOutPlayerInfo.java +++ b/net/minecraft/server/PacketPlayOutPlayerInfo.java -@@ -14,6 +14,20 @@ - private PacketPlayOutPlayerInfo.EnumPlayerInfoAction a; - private final List b = Lists.newArrayList(); +@@ -9,7 +9,36 @@ + import java.util.List; + import javax.annotation.Nullable; -+ // SportBukkit start - add constructor +-public class PacketPlayOutPlayerInfo implements Packet { ++// SportBukkit start ++import java.util.UUID; ++ ++import net.md_5.bungee.api.chat.BaseComponent; ++import org.bukkit.GameMode; ++import org.bukkit.Skin; ++import org.bukkit.craftbukkit.protocol.RenderableComponent; ++import org.bukkit.craftbukkit.protocol.Renderable; ++import org.bukkit.craftbukkit.util.Skins; ++import org.bukkit.entity.Player; ++ ++public class PacketPlayOutPlayerInfo implements Packet, Renderable { ++ ++ @Override ++ public void render(Player viewer) { ++ b.forEach(info -> RenderableComponent.render(info.e, viewer)); ++ } ++ + public PacketPlayOutPlayerInfo(EnumPlayerInfoAction a) { + this.a = a; + } @@ -17,48 +35,37 @@ + return this.b.isEmpty(); + } + // SportBukkit end -+ - public PacketPlayOutPlayerInfo() {} - - public PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction packetplayoutplayerinfo_enumplayerinfoaction, EntityPlayer... aentityplayer) { -@@ -130,6 +144,15 @@ - packetdataserializer.d(packetplayoutplayerinfo_playerinfodata.c().getId()); - packetdataserializer.d(packetplayoutplayerinfo_playerinfodata.b()); -+ -+ // SportBukkit start -+ if (packetplayoutplayerinfo_playerinfodata.displayName != null) { -+ packetdataserializer.writeBoolean(true); -+ packetdataserializer.a(net.md_5.bungee.chat.ComponentSerializer.toString(packetplayoutplayerinfo_playerinfodata.displayName)); -+ break; -+ } -+ // SportBukkit end -+ - if (packetplayoutplayerinfo_playerinfodata.d() == null) { - packetdataserializer.writeBoolean(false); - } else { -@@ -150,6 +173,15 @@ - - case UPDATE_DISPLAY_NAME: - packetdataserializer.a(packetplayoutplayerinfo_playerinfodata.a().getId()); -+ -+ // SportBukkit start -+ if (packetplayoutplayerinfo_playerinfodata.displayName != null) { -+ packetdataserializer.writeBoolean(true); -+ packetdataserializer.a(net.md_5.bungee.chat.ComponentSerializer.toString(packetplayoutplayerinfo_playerinfodata.displayName)); -+ break; -+ } -+ // SportBukkit end -+ - if (packetplayoutplayerinfo_playerinfodata.d() == null) { - packetdataserializer.writeBoolean(false); - } else { -@@ -175,6 +207,8 @@ + private PacketPlayOutPlayerInfo.EnumPlayerInfoAction a; + private final List b = Lists.newArrayList(); +@@ -175,6 +204,21 @@ public class PlayerInfoData { -+ public net.md_5.bungee.api.chat.BaseComponent[] displayName; // SportBukkit ++ // SportBukkit start ++ public PlayerInfoData(UUID uuid, String name, @Nullable BaseComponent displayName, GameMode gamemode, int ping, @Nullable Skin skin) { ++ this(new GameProfile(uuid, name), ++ ping, ++ EnumGamemode.getById(gamemode.getValue()), ++ displayName == null ? null : new RenderableComponent(displayName)); ++ ++ if(skin != null) { ++ Skins.toProperties(skin).asMap().forEach( ++ (k, v) -> d.getProperties().putAll(k, v) ++ ); ++ } ++ } ++ // SportBukkit end + private final int b; private final EnumGamemode c; private final GameProfile d; +@@ -184,7 +228,7 @@ + this.d = gameprofile; + this.b = i; + this.c = enumgamemode; +- this.e = ichatbasecomponent; ++ this.e = RenderableComponent.wrap(ichatbasecomponent); // SportBukkit + } + + public GameProfile a() { diff --git a/snapshot/CraftBukkit/nms-patches/PacketPlayOutPlayerListHeaderFooter.patch b/snapshot/CraftBukkit/nms-patches/PacketPlayOutPlayerListHeaderFooter.patch index 1ce7f747..0db85c09 100644 --- a/snapshot/CraftBukkit/nms-patches/PacketPlayOutPlayerListHeaderFooter.patch +++ b/snapshot/CraftBukkit/nms-patches/PacketPlayOutPlayerListHeaderFooter.patch @@ -1,33 +1,29 @@ --- a/net/minecraft/server/PacketPlayOutPlayerListHeaderFooter.java +++ b/net/minecraft/server/PacketPlayOutPlayerListHeaderFooter.java -@@ -4,6 +4,8 @@ +@@ -2,7 +2,25 @@ - public class PacketPlayOutPlayerListHeaderFooter implements Packet { + import java.io.IOException; -+ public net.md_5.bungee.api.chat.BaseComponent[] header, footer; // SportBukkit +-public class PacketPlayOutPlayerListHeaderFooter implements Packet { ++// SportBukkit start ++import net.md_5.bungee.api.chat.BaseComponent; ++import org.bukkit.craftbukkit.protocol.RenderableComponent; ++import org.bukkit.craftbukkit.protocol.Renderable; ++import org.bukkit.entity.Player; + - private IChatBaseComponent a; - private IChatBaseComponent b; - -@@ -19,8 +21,19 @@ - } - - public void b(PacketDataSerializer packetdataserializer) throws IOException { -- packetdataserializer.a(this.a); -- packetdataserializer.a(this.b); -+ // SportBukkit start -+ if(this.header != null) { -+ packetdataserializer.a(net.md_5.bungee.chat.ComponentSerializer.toString(this.header)); -+ } else { -+ packetdataserializer.a(this.a); -+ } ++public class PacketPlayOutPlayerListHeaderFooter implements Packet, Renderable { + -+ if(this.footer != null) { -+ packetdataserializer.a(net.md_5.bungee.chat.ComponentSerializer.toString(this.footer)); -+ } else { -+ packetdataserializer.a(this.b); -+ } -+ // SportBukkit end - } ++ @Override ++ public void render(Player viewer) { ++ RenderableComponent.render(a, viewer); ++ RenderableComponent.render(b, viewer); ++ } ++ ++ public PacketPlayOutPlayerListHeaderFooter(BaseComponent header, BaseComponent footer) { ++ a = new RenderableComponent(header); ++ b = new RenderableComponent(footer); ++ } ++ // SportBukkit end - public void a(PacketListenerPlayOut packetlistenerplayout) { + private IChatBaseComponent a; + private IChatBaseComponent b; diff --git a/snapshot/CraftBukkit/nms-patches/PacketPlayOutTitle.patch b/snapshot/CraftBukkit/nms-patches/PacketPlayOutTitle.patch index 406def9e..6f71f9ee 100644 --- a/snapshot/CraftBukkit/nms-patches/PacketPlayOutTitle.patch +++ b/snapshot/CraftBukkit/nms-patches/PacketPlayOutTitle.patch @@ -1,25 +1,40 @@ --- a/net/minecraft/server/PacketPlayOutTitle.java +++ b/net/minecraft/server/PacketPlayOutTitle.java -@@ -12,6 +12,18 @@ - private int d; - private int e; +@@ -4,7 +4,23 @@ + import java.util.Locale; + import javax.annotation.Nullable; -+ // SportBukkit start -+ public net.md_5.bungee.api.chat.BaseComponent[] components; +-public class PacketPlayOutTitle implements Packet { ++// SportBukkit start ++import net.md_5.bungee.api.chat.BaseComponent; ++import org.bukkit.craftbukkit.protocol.RenderableComponent; ++import org.bukkit.craftbukkit.protocol.Renderable; ++import org.bukkit.entity.Player; + -+ public PacketPlayOutTitle(EnumTitleAction action, net.md_5.bungee.api.chat.BaseComponent[] components, int fadeIn, int stay, int fadeOut) { -+ this.a = action; -+ this.components = components; -+ this.c = fadeIn; -+ this.d = stay; -+ this.e = fadeOut; ++public class PacketPlayOutTitle implements Packet, Renderable { ++ ++ @Override ++ public void render(Player viewer) { ++ RenderableComponent.render(b, viewer); + } -+ // SportBukkit end + - public PacketPlayOutTitle() {} ++ public PacketPlayOutTitle(EnumTitleAction action, @Nullable BaseComponent text, int fadeIn, int stay, int fadeOut) { ++ this(action, text == null ? null : new RenderableComponent(text), fadeIn, stay, fadeOut); ++ } ++ // SportBukkit end + + private PacketPlayOutTitle.EnumTitleAction a; + private IChatBaseComponent b; +@@ -24,7 +40,7 @@ - public PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction packetplayouttitle_enumtitleaction, IChatBaseComponent ichatbasecomponent) { -@@ -45,9 +57,22 @@ + public PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction packetplayouttitle_enumtitleaction, @Nullable IChatBaseComponent ichatbasecomponent, int i, int j, int k) { + this.a = packetplayouttitle_enumtitleaction; +- this.b = ichatbasecomponent; ++ this.b = RenderableComponent.wrap(ichatbasecomponent); // SportBukkit + this.c = i; + this.d = j; + this.e = k; +@@ -45,7 +61,14 @@ } public void b(PacketDataSerializer packetdataserializer) throws IOException { @@ -33,14 +48,5 @@ + } + // SportBukkit end if (this.a == PacketPlayOutTitle.EnumTitleAction.TITLE || this.a == PacketPlayOutTitle.EnumTitleAction.SUBTITLE || this.a == PacketPlayOutTitle.EnumTitleAction.ACTIONBAR) { -- packetdataserializer.a(this.b); -+ // SportBukkit start -+ if(this.components != null) { -+ packetdataserializer.a(net.md_5.bungee.chat.ComponentSerializer.toString(components)); -+ } else { -+ packetdataserializer.a(this.b); -+ } -+ // SportBukkit end + packetdataserializer.a(this.b); } - - if (this.a == PacketPlayOutTitle.EnumTitleAction.TIMES) { diff --git a/snapshot/CraftBukkit/nms-patches/PlayerConnection.patch b/snapshot/CraftBukkit/nms-patches/PlayerConnection.patch index 6c65361c..70ade4ad 100644 --- a/snapshot/CraftBukkit/nms-patches/PlayerConnection.patch +++ b/snapshot/CraftBukkit/nms-patches/PlayerConnection.patch @@ -1,6 +1,14 @@ --- a/net/minecraft/server/PlayerConnection.java +++ b/net/minecraft/server/PlayerConnection.java -@@ -11,10 +11,64 @@ +@@ -3,7 +3,6 @@ + import com.google.common.collect.Lists; + import com.google.common.primitives.Doubles; + import com.google.common.primitives.Floats; +-import com.google.common.util.concurrent.Futures; + import io.netty.util.concurrent.Future; + import io.netty.util.concurrent.GenericFutureListener; + import java.io.IOException; +@@ -11,10 +10,65 @@ import java.util.Collections; import java.util.Iterator; import java.util.Set; @@ -24,6 +32,7 @@ +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.craftbukkit.protocol.Renderable; +import org.bukkit.craftbukkit.util.BungeeChatUtils; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.craftbukkit.util.LazyPlayerSet; @@ -863,7 +872,7 @@ if (packet instanceof PacketPlayOutChat) { PacketPlayOutChat packetplayoutchat = (PacketPlayOutChat) packet; EntityHuman.EnumChatVisibility entityhuman_enumchatvisibility = this.player.getChatFlags(); -@@ -602,8 +1179,92 @@ +@@ -602,8 +1179,96 @@ } } @@ -948,6 +957,10 @@ + } + } + } ++ ++ } ++ if(packet instanceof Renderable) { ++ ((Renderable) packet).render(getPlayer()); + } + // CraftBukkit end + @@ -957,7 +970,7 @@ } catch (Throwable throwable) { CrashReport crashreport = CrashReport.a(throwable, "Sending packet"); CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Packet being sent"); -@@ -623,17 +1284,32 @@ +@@ -623,17 +1288,32 @@ public void a(PacketPlayInHeldItemSlot packetplayinhelditemslot) { PlayerConnectionUtils.ensureMainThread(packetplayinhelditemslot, this, this.player.x()); @@ -992,7 +1005,7 @@ ChatMessage chatmessage = new ChatMessage("chat.cannotSend", new Object[0]); chatmessage.getChatModifier().setColor(EnumChatFormat.RED); -@@ -646,39 +1322,251 @@ +@@ -646,39 +1326,251 @@ for (int i = 0; i < s.length(); ++i) { if (!SharedConstants.isAllowedChatCharacter(s.charAt(i))) { @@ -1252,7 +1265,7 @@ this.player.resetIdleTimer(); IJumpable ijumpable; -@@ -750,6 +1638,7 @@ +@@ -750,6 +1642,7 @@ public void a(PacketPlayInUseEntity packetplayinuseentity) { PlayerConnectionUtils.ensureMainThread(packetplayinuseentity, this, this.player.x()); @@ -1260,7 +1273,7 @@ WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); Entity entity = packetplayinuseentity.a((World) worldserver); -@@ -765,24 +1654,87 @@ +@@ -765,24 +1658,87 @@ if (this.player.h(entity) < d0) { EnumHand enumhand; @@ -1350,7 +1363,7 @@ } public void a(PacketPlayInClientCommand packetplayinclientcommand) { -@@ -794,7 +1746,8 @@ +@@ -794,7 +1750,8 @@ case PERFORM_RESPAWN: if (this.player.viewingCredits) { this.player.viewingCredits = false; @@ -1360,7 +1373,7 @@ } else { if (this.player.getHealth() > 0.0F) { return; -@@ -820,14 +1773,21 @@ +@@ -820,14 +1777,21 @@ public void a(PacketPlayInCloseWindow packetplayinclosewindow) { PlayerConnectionUtils.ensureMainThread(packetplayinclosewindow, this, this.player.x()); @@ -1383,7 +1396,7 @@ NonNullList nonnulllist = NonNullList.a(); for (int i = 0; i < this.player.activeContainer.c.size(); ++i) { -@@ -836,8 +1796,281 @@ +@@ -836,8 +1800,281 @@ this.player.a(this.player.activeContainer, nonnulllist); } else { @@ -1395,7 +1408,7 @@ + + InventoryView inventory = this.player.activeContainer.getBukkitView(); + SlotType type = CraftInventoryView.getSlotType(inventory, packetplayinwindowclick.b()); - ++ + InventoryClickEvent event; + ClickType click = ClickType.UNKNOWN; + InventoryAction action = InventoryAction.UNKNOWN; @@ -1655,7 +1668,7 @@ + + server.getPluginManager().callEvent(new org.bukkit.event.inventory.InventoryClickedEvent(inventory, type, packetplayinwindowclick.b(), packetplayinwindowclick.c() != 0, packetplayinwindowclick.f() == InventoryClickType.QUICK_MOVE)); + if(event.getResult() == org.bukkit.event.Event.Result.DENY) return; -+ + + if (event instanceof CraftItemEvent) { + // Need to update the inventory on crafting to + // correctly support custom recipes @@ -1666,7 +1679,7 @@ if (ItemStack.matches(packetplayinwindowclick.e(), itemstack)) { this.player.playerConnection.sendPacket(new PacketPlayOutTransaction(packetplayinwindowclick.a(), packetplayinwindowclick.d(), true)); this.player.f = true; -@@ -866,6 +2099,7 @@ +@@ -866,6 +2103,7 @@ public void a(PacketPlayInEnchantItem packetplayinenchantitem) { PlayerConnectionUtils.ensureMainThread(packetplayinenchantitem, this, this.player.x()); @@ -1674,7 +1687,7 @@ this.player.resetIdleTimer(); if (this.player.activeContainer.windowId == packetplayinenchantitem.a() && this.player.activeContainer.c(this.player) && !this.player.isSpectator()) { this.player.activeContainer.a(this.player, packetplayinenchantitem.b()); -@@ -899,7 +2133,46 @@ +@@ -899,7 +2137,46 @@ } boolean flag1 = packetplayinsetcreativeslot.a() >= 1 && packetplayinsetcreativeslot.a() <= 45; @@ -1722,7 +1735,7 @@ if (flag1 && flag2) { if (itemstack.isEmpty()) { -@@ -923,6 +2196,7 @@ +@@ -923,6 +2200,7 @@ public void a(PacketPlayInTransaction packetplayintransaction) { PlayerConnectionUtils.ensureMainThread(packetplayintransaction, this, this.player.x()); @@ -1730,7 +1743,7 @@ Short oshort = (Short) this.k.get(this.player.activeContainer.windowId); if (oshort != null && packetplayintransaction.b() == oshort.shortValue() && this.player.activeContainer.windowId == packetplayintransaction.a() && !this.player.activeContainer.c(this.player) && !this.player.isSpectator()) { -@@ -933,6 +2207,7 @@ +@@ -933,6 +2211,7 @@ public void a(PacketPlayInUpdateSign packetplayinupdatesign) { PlayerConnectionUtils.ensureMainThread(packetplayinupdatesign, this, this.player.x()); @@ -1738,7 +1751,7 @@ this.player.resetIdleTimer(); WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); BlockPosition blockposition = packetplayinupdatesign.a(); -@@ -949,14 +2224,32 @@ +@@ -949,14 +2228,32 @@ if (!tileentitysign.a() || tileentitysign.e() != this.player) { this.minecraftServer.warning("Player " + this.player.getName() + " just tried to change non-editable sign"); @@ -1772,7 +1785,7 @@ tileentitysign.update(); worldserver.notify(blockposition, iblockdata, iblockdata, 3); -@@ -965,6 +2258,7 @@ +@@ -965,6 +2262,7 @@ } public void a(PacketPlayInKeepAlive packetplayinkeepalive) { @@ -1780,7 +1793,7 @@ if (packetplayinkeepalive.a() == this.f) { int i = (int) (this.e() - this.g); -@@ -973,17 +2267,35 @@ +@@ -973,17 +2271,35 @@ } @@ -1817,7 +1830,7 @@ ArrayList arraylist = Lists.newArrayList(); Iterator iterator = this.minecraftServer.tabCompleteCommand(this.player, packetplayintabcomplete.a(), packetplayintabcomplete.b(), packetplayintabcomplete.c()).iterator(); -@@ -1027,10 +2339,13 @@ +@@ -1027,10 +2343,13 @@ } if (itemstack.getItem() == Items.WRITABLE_BOOK && itemstack.getItem() == itemstack1.getItem()) { @@ -1831,7 +1844,7 @@ } } else { String s1; -@@ -1069,10 +2384,11 @@ +@@ -1069,10 +2388,11 @@ } itemstack2.a("pages", (NBTBase) nbttaglist); @@ -1844,7 +1857,7 @@ } } else if ("MC|TrSel".equals(s)) { try { -@@ -1084,6 +2400,7 @@ +@@ -1084,6 +2404,7 @@ } } catch (Exception exception2) { PlayerConnection.LOGGER.error("Couldn\'t select trade", exception2); @@ -1852,7 +1865,7 @@ } } else { TileEntity tileentity; -@@ -1133,6 +2450,7 @@ +@@ -1133,6 +2454,7 @@ } } catch (Exception exception3) { PlayerConnection.LOGGER.error("Couldn\'t set command block", exception3); @@ -1860,7 +1873,7 @@ } } else if ("MC|AutoCmd".equals(s)) { if (!this.minecraftServer.getEnableCommandBlock()) { -@@ -1200,6 +2518,7 @@ +@@ -1200,6 +2522,7 @@ } } catch (Exception exception4) { PlayerConnection.LOGGER.error("Couldn\'t set command block", exception4); @@ -1868,7 +1881,7 @@ } } else { int k; -@@ -1223,6 +2542,7 @@ +@@ -1223,6 +2546,7 @@ } } catch (Exception exception5) { PlayerConnection.LOGGER.error("Couldn\'t set beacon", exception5); @@ -1876,7 +1889,7 @@ } } } else if ("MC|ItemName".equals(s)) { -@@ -1276,10 +2596,13 @@ +@@ -1276,10 +2600,13 @@ tileentitystructure.b(EnumBlockRotation.valueOf(s6)); tileentitystructure.b(packetdataserializer.e(128)); tileentitystructure.a(packetdataserializer.readBoolean()); @@ -1890,7 +1903,7 @@ String s7 = tileentitystructure.a(); if (b1 == 2) { -@@ -1309,6 +2632,7 @@ +@@ -1309,6 +2636,7 @@ } } catch (Exception exception6) { PlayerConnection.LOGGER.error("Couldn\'t set structure block", exception6); @@ -1898,7 +1911,7 @@ } } else if ("MC|PickItem".equals(s)) { packetdataserializer = packetplayincustompayload.b(); -@@ -1323,9 +2647,31 @@ +@@ -1323,9 +2651,31 @@ PlayerConnection.LOGGER.error("Couldn\'t pick item", exception7); } } diff --git a/snapshot/CraftBukkit/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/snapshot/CraftBukkit/src/main/java/org/bukkit/craftbukkit/CraftServer.java index e51f6061..b47ce398 100644 --- a/snapshot/CraftBukkit/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/snapshot/CraftBukkit/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -24,6 +24,7 @@ import java.util.regex.Pattern; import javax.annotation.Nullable; import javax.imageio.ImageIO; +import javax.inject.Provider; import com.google.common.collect.Maps; import com.google.inject.Guice; @@ -145,6 +146,8 @@ import net.md_5.bungee.api.chat.BaseComponent; import tc.oc.minecraft.api.configuration.InvalidConfigurationException; import tc.oc.minecraft.api.plugin.PluginFinder; +import tc.oc.minecraft.api.text.TextRenderContext; +import tc.oc.minecraft.api.text.TextRenderer; public final class CraftServer extends CraftBukkitRuntime implements Server { private static final Player[] EMPTY_PLAYER_ARRAY = new Player[0]; @@ -2000,4 +2003,23 @@ public void checkEmpty() { } } } + + @Override + public TextRenderContext textRenderContext() { + return textRenderContext; + } + + private final TextRenderContext textRenderContext = new TextRenderContext() { + @Override + public BaseComponent render(BaseComponent text, tc.oc.minecraft.api.command.CommandSender viewer) { + for(Plugin plugin : getPluginManager().getPlugins()) { + if(plugin.isEnabled()) { + for(Provider renderer : plugin.textRenderers()) { + text = renderer.get().render(this, text, viewer); + } + } + } + return text; + } + }; } diff --git a/snapshot/CraftBukkit/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/snapshot/CraftBukkit/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index 5cb0c168..7260a623 100644 --- a/snapshot/CraftBukkit/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/snapshot/CraftBukkit/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -1898,76 +1898,60 @@ public java.util.Locale getCurrentLocale() { } @Override - public void sendMessage(net.md_5.bungee.api.ChatMessageType position, BaseComponent... message) { + public void sendMessage(net.md_5.bungee.api.ChatMessageType position, BaseComponent message) { if ( getHandle().playerConnection == null ) return; - - PacketPlayOutChat packet = new PacketPlayOutChat(null, (byte) position.ordinal()); - if(position == ChatMessageType.ACTION_BAR) { - // Work around a client bug where component text above the hotbar is not formatted. - // The only way to format it is by wrapping legacy formatting in a text component. - packet.components = new BaseComponent[]{ new TextComponent(TextComponent.toLegacyText(message)) }; - } else { - packet.components = message; - } - getHandle().playerConnection.sendPacket(packet); + getHandle().playerConnection.sendPacket(new PacketPlayOutChat(message, position)); } @Override - public void sendMessage(net.md_5.bungee.api.ChatMessageType position, BaseComponent message) { - sendMessage(position, new BaseComponent[] {message}); + public void sendMessage(net.md_5.bungee.api.ChatMessageType position, BaseComponent... messages) { + sendMessage(position, new TextComponent(messages)); } @Override public void sendMessage(BaseComponent component) { - sendMessage( new BaseComponent[] { component } ); + sendMessage(ChatMessageType.CHAT, component); } @Override public void sendMessage(BaseComponent... components) { - if ( getHandle().playerConnection == null ) return; - - PacketPlayOutChat packet = new PacketPlayOutChat(); - packet.components = components; - getHandle().playerConnection.sendPacket(packet); + sendMessage(ChatMessageType.CHAT, components); } @Override - public void setPlayerListHeaderFooter(BaseComponent[] header, BaseComponent[] footer) { - PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter(); - packet.header = header; - packet.footer = footer; - getHandle().playerConnection.sendPacket(packet); + public void setPlayerListHeaderFooter(BaseComponent header, BaseComponent footer) { + getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerListHeaderFooter(header, footer)); } @Override - public void setPlayerListHeaderFooter(BaseComponent header, BaseComponent footer) { - this.setPlayerListHeaderFooter(header == null ? null : new BaseComponent[]{ header }, - footer == null ? null : new BaseComponent[]{ footer }); + public void setPlayerListHeaderFooter(BaseComponent[] header, BaseComponent[] footer) { + this.setPlayerListHeaderFooter(header == null ? null : new TextComponent(header), + footer == null ? null : new TextComponent(footer)); } @Override public void setTitleTimes(int fadeInTicks, int stayTicks, int fadeOutTicks) { - getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.TIMES, (BaseComponent[]) null, fadeInTicks, stayTicks, fadeOutTicks)); + getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.TIMES, (BaseComponent) null, fadeInTicks, stayTicks, fadeOutTicks)); } @Override public void setSubtitle(BaseComponent[] subtitle) { - getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.SUBTITLE, subtitle, 0, 0, 0)); + setSubtitle(new TextComponent(subtitle)); } @Override public void setSubtitle(BaseComponent subtitle) { - setSubtitle(new BaseComponent[] { subtitle }); + getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.SUBTITLE, subtitle, 0, 0, 0)); } @Override public void showTitle(BaseComponent[] title) { - getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.TITLE, title, 0, 0, 0)); + showTitle(new TextComponent(title)); } @Override public void showTitle(BaseComponent title) { - showTitle(new BaseComponent[] {title}); + getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.TITLE, title, 0, 0, 0)); } @Override @@ -1986,7 +1970,7 @@ public void showTitle(BaseComponent title, BaseComponent subtitle, int fadeInTic @Override public void hideTitle() { - getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.CLEAR, (BaseComponent[]) null, 0, 0, 0)); + getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.CLEAR, (BaseComponent) null, 0, 0, 0)); } @Override diff --git a/snapshot/CraftBukkit/src/main/java/org/bukkit/craftbukkit/protocol/Renderable.java b/snapshot/CraftBukkit/src/main/java/org/bukkit/craftbukkit/protocol/Renderable.java new file mode 100644 index 00000000..875671a6 --- /dev/null +++ b/snapshot/CraftBukkit/src/main/java/org/bukkit/craftbukkit/protocol/Renderable.java @@ -0,0 +1,8 @@ +package org.bukkit.craftbukkit.protocol; + +import org.bukkit.entity.Player; + +public interface Renderable { + + void render(Player viewer); +} diff --git a/snapshot/CraftBukkit/src/main/java/org/bukkit/craftbukkit/protocol/RenderableComponent.java b/snapshot/CraftBukkit/src/main/java/org/bukkit/craftbukkit/protocol/RenderableComponent.java new file mode 100644 index 00000000..30183967 --- /dev/null +++ b/snapshot/CraftBukkit/src/main/java/org/bukkit/craftbukkit/protocol/RenderableComponent.java @@ -0,0 +1,69 @@ +package org.bukkit.craftbukkit.protocol; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import javax.annotation.Nullable; + +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.chat.ComponentSerializer; +import net.minecraft.server.ChatBaseComponent; +import net.minecraft.server.IChatBaseComponent; +import net.minecraft.server.PacketDataSerializer; +import org.bukkit.craftbukkit.util.BungeeChatUtils; +import org.bukkit.entity.Player; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class RenderableComponent extends ChatBaseComponent { + + private final BaseComponent original; + private final Map rendered = new HashMap<>(); + + public static @Nullable RenderableComponent wrap(@Nullable IChatBaseComponent nms) { + if(nms == null) return null; + if(nms instanceof RenderableComponent) { + return (RenderableComponent) nms; + } + return new RenderableComponent(BungeeChatUtils.toBungee(nms)); + } + + public static void render(@Nullable IChatBaseComponent nms, Player viewer) { + if(nms instanceof RenderableComponent) { + ((RenderableComponent) nms).render(viewer); + } + } + + public RenderableComponent(BaseComponent bukkit) { + this.original = checkNotNull(bukkit); + } + + protected BaseComponent afterRender(BaseComponent text) { + return text; + } + + public void render(Player viewer) { + rendered.computeIfAbsent( + viewer.getUniqueId(), + uuid -> afterRender( + viewer.getServer() + .textRenderContext() + .render(original, viewer) + ) + ); + } + + public void serialize(PacketDataSerializer data) { + data.a(ComponentSerializer.toString(rendered.getOrDefault(data.playerId, original))); + } + + @Override + public String getText() { + throw new UnsupportedOperationException(); + } + + @Override + public IChatBaseComponent f() { + throw new UnsupportedOperationException(); + } +}