From 1acaddf677322a05af0af8fcd72e643756af589b Mon Sep 17 00:00:00 2001 From: mrapple Date: Sat, 6 Sep 2014 19:38:31 -0500 Subject: [PATCH 01/15] Derploy --- pom.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pom.xml b/pom.xml index 38975e8701f..8e5872034b0 100644 --- a/pom.xml +++ b/pom.xml @@ -29,6 +29,16 @@ repo + + + overcast-deployment + https://repo.oc.tc/content/repositories/releases + + + overcast-deployment + https://repo.oc.tc/content/repositories/snapshots + + From 05b98db5c3bb0227c941fdbf18f1e4a4df51859d Mon Sep 17 00:00:00 2001 From: mrapple Date: Thu, 11 Sep 2014 23:33:52 -0500 Subject: [PATCH 02/15] Disable security manager --- proxy/src/main/java/net/md_5/bungee/BungeeCord.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java index 8fc2f88263e..d3b9e52b24a 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -171,7 +171,8 @@ public BungeeCord() throws IOException // Java uses ! to indicate a resource inside of a jar/zip/other container. Running Bungee from within a directory that has a ! will cause this to muck up. Preconditions.checkState( new File( "." ).getAbsolutePath().indexOf( '!' ) == -1, "Cannot use BungeeCord in directory with ! in path." ); - System.setSecurityManager( new BungeeSecurityManager() ); + // Overcast - disable security manager + // System.setSecurityManager( new BungeeSecurityManager() ); try { From 15cf3e206bc922a30843aa56306d769d5f3e35d5 Mon Sep 17 00:00:00 2001 From: Jedediah Smith Date: Wed, 19 Nov 2014 04:07:51 -0500 Subject: [PATCH 03/15] Log stderr at WARNING instead of SEVERE --- proxy/src/main/java/net/md_5/bungee/BungeeCord.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java index d3b9e52b24a..470957fc696 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -197,7 +197,9 @@ public BungeeCord() throws IOException consoleReader.setExpandEvents( false ); logger = new BungeeLogger( this ); - System.setErr( new PrintStream( new LoggingOutputStream( logger, Level.SEVERE ), true ) ); + + // Overcast - stderr gets a lot of non-error output, so log it at WARNING level instead of SEVERE + System.setErr( new PrintStream( new LoggingOutputStream( logger, Level.WARNING ), true ) ); System.setOut( new PrintStream( new LoggingOutputStream( logger, Level.INFO ), true ) ); if ( !Boolean.getBoolean( "net.md_5.bungee.native.disable" ) ) From 84b87fef63cf94b2bed8fcb5a6756cf23d4441a4 Mon Sep 17 00:00:00 2001 From: Jedediah Smith Date: Thu, 27 Nov 2014 17:40:43 -0500 Subject: [PATCH 04/15] Postpone PluginManager creation so that EventBus gets the right logger --- .../main/java/net/md_5/bungee/BungeeCord.java | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java index 470957fc696..07a707114e7 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -122,7 +122,7 @@ public class BungeeCord extends ProxyServer * Plugin manager. */ @Getter - public final PluginManager pluginManager = new PluginManager( this ); + public final PluginManager pluginManager; @Getter @Setter private ReconnectHandler reconnectHandler; @@ -148,18 +148,6 @@ public class BungeeCord extends ProxyServer private ConnectionThrottle connectionThrottle; private final ModuleManager moduleManager = new ModuleManager(); - - { - // TODO: Proper fallback when we interface the manager - getPluginManager().registerCommand( null, new CommandReload() ); - getPluginManager().registerCommand( null, new CommandEnd() ); - getPluginManager().registerCommand( null, new CommandIP() ); - getPluginManager().registerCommand( null, new CommandBungee() ); - getPluginManager().registerCommand( null, new CommandPerms() ); - - registerChannel( "BungeeCord" ); - } - public static BungeeCord getInstance() { return (BungeeCord) ProxyServer.getInstance(); @@ -219,6 +207,17 @@ public BungeeCord() throws IOException logger.info( "Using standard Java compressor. To enable zero copy compression, run on 64 bit Linux" ); } } + + pluginManager = new PluginManager( this ); + + // TODO: Proper fallback when we interface the manager + getPluginManager().registerCommand( null, new CommandReload() ); + getPluginManager().registerCommand( null, new CommandEnd() ); + getPluginManager().registerCommand( null, new CommandIP() ); + getPluginManager().registerCommand( null, new CommandBungee() ); + getPluginManager().registerCommand( null, new CommandPerms() ); + + registerChannel( "BungeeCord" ); } /** From c5f3e5b82ff3c6301799c6b07f0440a209d9247a Mon Sep 17 00:00:00 2001 From: Jedediah Smith Date: Thu, 27 Nov 2014 17:41:15 -0500 Subject: [PATCH 05/15] Log event exceptions at SEVERE level --- event/src/main/java/net/md_5/bungee/event/EventBus.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event/src/main/java/net/md_5/bungee/event/EventBus.java b/event/src/main/java/net/md_5/bungee/event/EventBus.java index cd4057f9609..7f1a3b1daa2 100644 --- a/event/src/main/java/net/md_5/bungee/event/EventBus.java +++ b/event/src/main/java/net/md_5/bungee/event/EventBus.java @@ -53,7 +53,7 @@ public void post(Object event) throw new Error( "Method rejected target/argument: " + event, ex ); } catch ( InvocationTargetException ex ) { - logger.log( Level.WARNING, MessageFormat.format( "Error dispatching event {0} to listener {1}", event, method.getListener() ), ex.getCause() ); + logger.log( Level.SEVERE, MessageFormat.format( "Error dispatching event {0} to listener {1}", event, method.getListener() ), ex.getCause() ); } } } From f1c572e1840cdb65970828836a491786d7f139d7 Mon Sep 17 00:00:00 2001 From: Jedediah Smith Date: Thu, 27 Nov 2014 21:18:59 -0500 Subject: [PATCH 06/15] Improve logging of packet decoding errors --- .../java/net/md_5/bungee/protocol/BadPacketException.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/BadPacketException.java b/protocol/src/main/java/net/md_5/bungee/protocol/BadPacketException.java index 6c0ef4dfa00..d3ee377d8b4 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/BadPacketException.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/BadPacketException.java @@ -1,6 +1,8 @@ package net.md_5.bungee.protocol; -public class BadPacketException extends RuntimeException +import io.netty.handler.codec.DecoderException; + +public class BadPacketException extends DecoderException { public BadPacketException(String message) From a227309ff1910151a2b8c24406ee7623b5c0997d Mon Sep 17 00:00:00 2001 From: Jedediah Smith Date: Sun, 30 Nov 2014 14:44:38 -0500 Subject: [PATCH 07/15] Guard against negative packet IDs --- protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java b/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java index 128d3934919..d55e3899ef9 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java @@ -122,7 +122,7 @@ public class DirectionData public boolean hasPacket(int id) { - return id < MAX_PACKET_ID && packetConstructors[id] != null; + return 0 <= id && id < MAX_PACKET_ID && packetConstructors[id] != null; } public final DefinedPacket createPacket(int id) From e5cba2a04cec143b63155a0922be524b9b3229c9 Mon Sep 17 00:00:00 2001 From: Jedediah Smith Date: Sun, 30 Nov 2014 15:02:58 -0500 Subject: [PATCH 08/15] A few more packet ID range checks --- .../java/net/md_5/bungee/protocol/Protocol.java | 4 ++-- .../java/net/md_5/bungee/entitymap/EntityMap.java | 14 +++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java b/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java index d55e3899ef9..1d6568911e3 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java @@ -100,7 +100,7 @@ public enum Protocol } }; /*========================================================================*/ - public static final int MAX_PACKET_ID = 0xFF; + public static final int MAX_PACKET_ID = 0x100; public static List supportedVersions = Arrays.asList( ProtocolConstants.MINECRAFT_1_7_2, ProtocolConstants.MINECRAFT_1_7_6, @@ -127,7 +127,7 @@ public boolean hasPacket(int id) public final DefinedPacket createPacket(int id) { - if ( id > MAX_PACKET_ID ) + if ( id < 0 || id >= MAX_PACKET_ID ) { throw new BadPacketException( "Packet with id " + id + " outside of range " ); } diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java index 56067db8cc8..47f96b1cd71 100644 --- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java +++ b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java @@ -3,6 +3,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.netty.buffer.ByteBuf; import net.md_5.bungee.protocol.DefinedPacket; +import net.md_5.bungee.protocol.Protocol; import net.md_5.bungee.protocol.ProtocolConstants; /** @@ -105,12 +106,15 @@ private static void rewrite(ByteBuf packet, int oldId, int newId, boolean[] ints int packetId = DefinedPacket.readVarInt( packet ); int packetIdLength = packet.readerIndex() - readerIndex; - if ( ints[ packetId ] ) + if ( 0 <= packetId && packetId < Protocol.MAX_PACKET_ID) { - rewriteInt( packet, oldId, newId, readerIndex + packetIdLength ); - } else if ( varints[ packetId ] ) - { - rewriteVarInt( packet, oldId, newId, readerIndex + packetIdLength ); + if ( ints[ packetId ] ) + { + rewriteInt( packet, oldId, newId, readerIndex + packetIdLength ); + } else if ( varints[ packetId ] ) + { + rewriteVarInt(packet, oldId, newId, readerIndex + packetIdLength); + } } packet.readerIndex( readerIndex ); } From 9404b2c87d865b9085e9157b9870fd5542cc50a8 Mon Sep 17 00:00:00 2001 From: Jedediah Smith Date: Sat, 13 Dec 2014 15:16:04 -0500 Subject: [PATCH 09/15] Ignore a common exception that appears to be harmless --- .../main/java/net/md_5/bungee/netty/ChannelWrapper.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java index 06d19c3c1b1..bb08aff2cff 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java @@ -12,6 +12,8 @@ import net.md_5.bungee.protocol.MinecraftEncoder; import net.md_5.bungee.protocol.Protocol; +import java.util.NoSuchElementException; + public class ChannelWrapper { @@ -78,7 +80,11 @@ public void setCompressionThreshold(int compressionThreshold) { if ( ch.pipeline().get( PacketCompressor.class ) == null && compressionThreshold != -1 ) { - addBefore( PipelineUtils.PACKET_ENCODER, "compress", new PacketCompressor() ); + try { + addBefore(PipelineUtils.PACKET_ENCODER, "compress", new PacketCompressor()); + } catch(NoSuchElementException ignored) { + // Sometimes packet-encoder is not in the pipeline, probably when the client disconnects soon after connecting + } } if ( compressionThreshold != -1 ) { From 8379dec5617ae6dd9bf3d4ecebc59b7808b56dca Mon Sep 17 00:00:00 2001 From: Jedediah Smith Date: Sun, 14 Dec 2014 02:58:43 -0500 Subject: [PATCH 10/15] Fix NPE caused by the other fix --- proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java index bb08aff2cff..955e7c9fcab 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java @@ -82,8 +82,9 @@ public void setCompressionThreshold(int compressionThreshold) { try { addBefore(PipelineUtils.PACKET_ENCODER, "compress", new PacketCompressor()); - } catch(NoSuchElementException ignored) { + } catch(NoSuchElementException ex) { // Sometimes packet-encoder is not in the pipeline, probably when the client disconnects soon after connecting + return; } } if ( compressionThreshold != -1 ) From 1d25cf2e2f47cf33c4e90e74ba74b69005c464c3 Mon Sep 17 00:00:00 2001 From: Jedediah Smith Date: Mon, 22 Dec 2014 12:37:59 -0500 Subject: [PATCH 11/15] Remove startup delay for outdated build --- bootstrap/src/main/java/net/md_5/bungee/BungeeCordLauncher.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/bootstrap/src/main/java/net/md_5/bungee/BungeeCordLauncher.java b/bootstrap/src/main/java/net/md_5/bungee/BungeeCordLauncher.java index 68f104b8072..625a18b1d3f 100644 --- a/bootstrap/src/main/java/net/md_5/bungee/BungeeCordLauncher.java +++ b/bootstrap/src/main/java/net/md_5/bungee/BungeeCordLauncher.java @@ -43,8 +43,6 @@ public static void main(String[] args) throws Exception System.err.println( "*** Warning, this build is outdated ***" ); System.err.println( "*** Please download a new build from http://ci.md-5.net/job/BungeeCord ***" ); System.err.println( "*** You will get NO support regarding this build ***" ); - System.err.println( "*** Server will start in 10 seconds ***" ); - Thread.sleep( TimeUnit.SECONDS.toMillis( 10 ) ); } } From d9edd25c058e0ab5ed693b60da58f6d96d23391e Mon Sep 17 00:00:00 2001 From: Jedediah Smith Date: Tue, 6 Jan 2015 11:24:09 -0500 Subject: [PATCH 12/15] Add the capability to simulate username changes --- .../java/net/md_5/bungee/api/event/ServerConnectEvent.java | 1 + proxy/src/main/java/net/md_5/bungee/ServerConnector.java | 4 +++- proxy/src/main/java/net/md_5/bungee/UserConnection.java | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/net/md_5/bungee/api/event/ServerConnectEvent.java b/api/src/main/java/net/md_5/bungee/api/event/ServerConnectEvent.java index 17cfccd0a0e..51824022bd2 100644 --- a/api/src/main/java/net/md_5/bungee/api/event/ServerConnectEvent.java +++ b/api/src/main/java/net/md_5/bungee/api/event/ServerConnectEvent.java @@ -34,6 +34,7 @@ public class ServerConnectEvent extends Event implements Cancellable * Cancelled state. */ private boolean cancelled; + private String fakeUsername; public ServerConnectEvent(ProxiedPlayer player, ServerInfo target) { diff --git a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java index 5282033a8d3..3740bf2e82a 100644 --- a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java +++ b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java @@ -35,6 +35,7 @@ import net.md_5.bungee.protocol.packet.Handshake; import net.md_5.bungee.protocol.packet.Kick; import net.md_5.bungee.protocol.packet.Login; +import net.md_5.bungee.protocol.packet.LoginRequest; import net.md_5.bungee.protocol.packet.LoginSuccess; import net.md_5.bungee.protocol.packet.PluginMessage; import net.md_5.bungee.protocol.packet.Respawn; @@ -50,6 +51,7 @@ public class ServerConnector extends PacketHandler private final UserConnection user; private final BungeeServerInfo target; private State thisState = State.LOGIN_SUCCESS; + private final String fakeUsername; @Getter private ForgeServerHandler handshakeHandler; @@ -102,7 +104,7 @@ else if ( !user.getExtraDataInHandshake().isEmpty() ) channel.write( copiedHandshake ); channel.setProtocol( Protocol.LOGIN ); - channel.write( user.getPendingConnection().getLoginRequest() ); + channel.write(new LoginRequest(fakeUsername != null ? fakeUsername : user.getPendingConnection().getName())); } @Override diff --git a/proxy/src/main/java/net/md_5/bungee/UserConnection.java b/proxy/src/main/java/net/md_5/bungee/UserConnection.java index fb437c57660..5f0484c4d00 100644 --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java @@ -227,6 +227,7 @@ public void connect(ServerInfo info, final Callback callback, final boo } final BungeeServerInfo target = (BungeeServerInfo) event.getTarget(); // Update in case the event changed target + final String fakeUsername = event.getFakeUsername(); if ( getServer() != null && Objects.equal( getServer().getInfo(), target ) ) { @@ -249,7 +250,7 @@ protected void initChannel(Channel ch) throws Exception PipelineUtils.BASE.initChannel( ch ); ch.pipeline().addAfter( PipelineUtils.FRAME_DECODER, PipelineUtils.PACKET_DECODER, new MinecraftDecoder( Protocol.HANDSHAKE, false, getPendingConnection().getVersion() ) ); ch.pipeline().addAfter( PipelineUtils.FRAME_PREPENDER, PipelineUtils.PACKET_ENCODER, new MinecraftEncoder( Protocol.HANDSHAKE, false, getPendingConnection().getVersion() ) ); - ch.pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( bungee, UserConnection.this, target ) ); + ch.pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( bungee, UserConnection.this, target, fakeUsername ) ); } }; ChannelFutureListener listener = new ChannelFutureListener() From af0d74aceaf701f3c7a8a1d40fd95899d1ace47f Mon Sep 17 00:00:00 2001 From: Jedediah Smith Date: Fri, 13 Feb 2015 02:28:52 -0500 Subject: [PATCH 13/15] Properly serialize all BaseComponent subclasses --- .../main/java/net/md_5/bungee/chat/ComponentSerializer.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chat/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java index 5756fc4afd8..8fbd4bcfdc0 100644 --- a/chat/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java +++ b/chat/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java @@ -18,9 +18,9 @@ public class ComponentSerializer implements JsonDeserializer { private final static Gson gson = new GsonBuilder(). - registerTypeAdapter( BaseComponent.class, new ComponentSerializer() ). - registerTypeAdapter( TextComponent.class, new TextComponentSerializer() ). - registerTypeAdapter( TranslatableComponent.class, new TranslatableComponentSerializer() ). + registerTypeHierarchyAdapter( BaseComponent.class, new ComponentSerializer() ). + registerTypeHierarchyAdapter( TextComponent.class, new TextComponentSerializer() ). + registerTypeHierarchyAdapter( TranslatableComponent.class, new TranslatableComponentSerializer() ). create(); public final static ThreadLocal> serializedComponents = new ThreadLocal>(); From fd275a90359901d5c6b1874ac427cc655d7648f2 Mon Sep 17 00:00:00 2001 From: Jedediah Smith Date: Sat, 27 Jun 2015 01:09:36 -0400 Subject: [PATCH 14/15] Improve toString method on BaseComponents --- .../md_5/bungee/api/chat/BaseComponent.java | 45 ++++++++++++++++++- .../md_5/bungee/api/chat/TextComponent.java | 8 ++-- .../api/chat/TranslatableComponent.java | 18 +++++++- .../bungee/chat/BaseComponentSerializer.java | 4 +- 4 files changed, 69 insertions(+), 6 deletions(-) diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java b/chat/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java index 4ae1654d2f2..821bbb7b8f4 100644 --- a/chat/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java +++ b/chat/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java @@ -1,5 +1,7 @@ package net.md_5.bungee.api.chat; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableSet; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -7,11 +9,13 @@ import net.md_5.bungee.api.ChatColor; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Set; + import lombok.ToString; @Setter -@ToString(exclude = "parent") @NoArgsConstructor public abstract class BaseComponent { @@ -388,4 +392,43 @@ void toLegacyText(StringBuilder builder) } } } + + protected void toString(List fields, Set visited) { + if(getColorRaw() != null) fields.add("color=" + getColorRaw()); + if(isBoldRaw() != null) fields.add("bold=" + isBoldRaw()); + if(isItalicRaw() != null) fields.add("italic=" + isItalicRaw()); + if(isUnderlinedRaw() != null) fields.add("underlined=" + isUnderlinedRaw()); + if(isStrikethroughRaw() != null) fields.add("strikethrough=" + isStrikethroughRaw()); + if(isObfuscatedRaw() != null) fields.add("obfuscated=" + isObfuscatedRaw()); + + if(getClickEvent() != null) fields.add("clickEvent=" + getClickEvent()); + if(getHoverEvent() != null) fields.add("hoverEvent=" + getHoverEvent()); + + if(getExtra() != null && !getExtra().isEmpty()) { + List extraText = new ArrayList(); + for(BaseComponent extra : getExtra()) { + extraText.add(extra.toString(visited)); + } + fields.add("extra=[" + Joiner.on(", ").join(extraText) + "]"); + } + } + + protected String toString(Set visited) { + String text = getClass().getSimpleName() + "{"; + if(visited.contains(this)) { + text += "..."; + } else { + visited = ImmutableSet.builder().addAll(visited).add(this).build(); + List fields = new ArrayList(); + toString(fields, visited); + text += Joiner.on(", ").join(fields); + } + + return text + "}"; + } + + @Override + public String toString() { + return toString(Collections.emptySet()); + } } diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/TextComponent.java b/chat/src/main/java/net/md_5/bungee/api/chat/TextComponent.java index 49214f8716a..4a0be74cb39 100644 --- a/chat/src/main/java/net/md_5/bungee/api/chat/TextComponent.java +++ b/chat/src/main/java/net/md_5/bungee/api/chat/TextComponent.java @@ -8,6 +8,8 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.List; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -206,8 +208,8 @@ protected void toLegacyText(StringBuilder builder) } @Override - public String toString() - { - return String.format( "TextComponent{text=%s, %s}", text, super.toString() ); + protected void toString(List fields, Set visited) { + fields.add("text=" + text); + super.toString(fields, visited); } } diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java b/chat/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java index c00b7fa94a9..9005c7b0824 100644 --- a/chat/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java +++ b/chat/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java @@ -1,5 +1,6 @@ package net.md_5.bungee.api.chat; +import com.google.common.base.Joiner; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -8,13 +9,13 @@ import java.util.List; import java.util.MissingResourceException; import java.util.ResourceBundle; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import lombok.ToString; @Getter @Setter -@ToString @NoArgsConstructor public class TranslatableComponent extends BaseComponent { @@ -240,4 +241,19 @@ private void addFormat(StringBuilder builder) builder.append( ChatColor.MAGIC ); } } + + @Override + protected void toString(List fields, Set visited) { + fields.add("translate=" + translate); + + if(getWith() != null && !getWith().isEmpty()) { + List withText = new ArrayList(); + for(BaseComponent with : getWith()) { + withText.add(with.toString(visited)); + } + fields.add("with=[" + Joiner.on(", ").join(withText) + "]"); + } + + super.toString(fields, visited); + } } diff --git a/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java index cb1d24a25d9..3d3919343dd 100644 --- a/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java +++ b/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java @@ -82,7 +82,9 @@ protected void serialize(JsonObject object, BaseComponent component, JsonSeriali } try { - Preconditions.checkArgument( !ComponentSerializer.serializedComponents.get().contains( component ), "Component loop" ); + if(ComponentSerializer.serializedComponents.get().contains( component )) { + throw new IllegalArgumentException("Component loop: " + component); + } ComponentSerializer.serializedComponents.get().add( component ); if ( component.getColorRaw() != null ) { From 24b58ea0aa2ab7e155e77e164da490c61222054c Mon Sep 17 00:00:00 2001 From: Jedediah Smith Date: Sat, 18 Jul 2015 19:38:55 -0400 Subject: [PATCH 15/15] Get rid of parent field in BaseComponent --- .../md_5/bungee/api/chat/BaseComponent.java | 244 +-------------- .../net/md_5/bungee/api/chat/Formatting.java | 277 ++++++++++++++++++ .../md_5/bungee/api/chat/TextComponent.java | 18 +- .../api/chat/TranslatableComponent.java | 37 ++- 4 files changed, 310 insertions(+), 266 deletions(-) create mode 100644 chat/src/main/java/net/md_5/bungee/api/chat/Formatting.java diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java b/chat/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java index 821bbb7b8f4..77752778a77 100644 --- a/chat/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java +++ b/chat/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java @@ -2,7 +2,6 @@ import com.google.common.base.Joiner; import com.google.common.collect.ImmutableSet; -import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -13,45 +12,9 @@ import java.util.List; import java.util.Set; -import lombok.ToString; - @Setter @NoArgsConstructor -public abstract class BaseComponent -{ - - @Setter(AccessLevel.NONE) - BaseComponent parent; - - /** - * The color of this component and any child components (unless overridden) - */ - private ChatColor color; - /** - * Whether this component and any child components (unless overridden) is - * bold - */ - private Boolean bold; - /** - * Whether this component and any child components (unless overridden) is - * italic - */ - private Boolean italic; - /** - * Whether this component and any child components (unless overridden) is - * underlined - */ - private Boolean underlined; - /** - * Whether this component and any child components (unless overridden) is - * strikethrough - */ - private Boolean strikethrough; - /** - * Whether this component and any child components (unless overridden) is - * obfuscated - */ - private Boolean obfuscated; +public abstract class BaseComponent extends Formatting { /** * Appended components that inherit this component's formatting and events @@ -59,19 +22,6 @@ public abstract class BaseComponent @Getter private List extra; - /** - * The action to preform when this component (and child components) are - * clicked - */ - @Getter - private ClickEvent clickEvent; - /** - * The action to preform when this component (and child components) are - * hovered over - */ - @Getter - private HoverEvent hoverEvent; - BaseComponent(BaseComponent old) { setColor( old.getColorRaw() ); @@ -131,178 +81,8 @@ public static String toPlainText(BaseComponent... components) return builder.toString(); } - /** - * Returns the color of this component. This uses the parent's color if this - * component doesn't have one. {@link net.md_5.bungee.api.ChatColor#WHITE} - * is returned if no color is found. - * - * @return the color of this component - */ - public ChatColor getColor() - { - if ( color == null ) - { - if ( parent == null ) - { - return ChatColor.WHITE; - } - return parent.getColor(); - } - return color; - } - - /** - * Returns the color of this component without checking the parents color. - * May return null - * - * @return the color of this component - */ - public ChatColor getColorRaw() - { - return color; - } - - /** - * Returns whether this component is bold. This uses the parent's setting if - * this component hasn't been set. false is returned if none of the parent - * chain has been set. - * - * @return whether the component is bold - */ - public boolean isBold() - { - if ( bold == null ) - { - return parent != null && parent.isBold(); - } - return bold; - } - - /** - * Returns whether this component is bold without checking the parents - * setting. May return null - * - * @return whether the component is bold - */ - public Boolean isBoldRaw() - { - return bold; - } - - /** - * Returns whether this component is italic. This uses the parent's setting - * if this component hasn't been set. false is returned if none of the - * parent chain has been set. - * - * @return whether the component is italic - */ - public boolean isItalic() - { - if ( italic == null ) - { - return parent != null && parent.isItalic(); - } - return italic; - } - - /** - * Returns whether this component is italic without checking the parents - * setting. May return null - * - * @return whether the component is italic - */ - public Boolean isItalicRaw() - { - return italic; - } - - /** - * Returns whether this component is underlined. This uses the parent's - * setting if this component hasn't been set. false is returned if none of - * the parent chain has been set. - * - * @return whether the component is underlined - */ - public boolean isUnderlined() - { - if ( underlined == null ) - { - return parent != null && parent.isUnderlined(); - } - return underlined; - } - - /** - * Returns whether this component is underlined without checking the parents - * setting. May return null - * - * @return whether the component is underlined - */ - public Boolean isUnderlinedRaw() - { - return underlined; - } - - /** - * Returns whether this component is strikethrough. This uses the parent's - * setting if this component hasn't been set. false is returned if none of - * the parent chain has been set. - * - * @return whether the component is strikethrough - */ - public boolean isStrikethrough() - { - if ( strikethrough == null ) - { - return parent != null && parent.isStrikethrough(); - } - return strikethrough; - } - - /** - * Returns whether this component is strikethrough without checking the - * parents setting. May return null - * - * @return whether the component is strikethrough - */ - public Boolean isStrikethroughRaw() - { - return strikethrough; - } - - /** - * Returns whether this component is obfuscated. This uses the parent's - * setting if this component hasn't been set. false is returned if none of - * the parent chain has been set. - * - * @return whether the component is obfuscated - */ - public boolean isObfuscated() - { - if ( obfuscated == null ) - { - return parent != null && parent.isObfuscated(); - } - return obfuscated; - } - - /** - * Returns whether this component is obfuscated without checking the parents - * setting. May return null - * - * @return whether the component is obfuscated - */ - public Boolean isObfuscatedRaw() - { - return obfuscated; - } - public void setExtra(List components) { - for ( BaseComponent component : components ) - { - component.parent = this; - } extra = components; } @@ -329,23 +109,9 @@ public void addExtra(BaseComponent component) { extra = new ArrayList(); } - component.parent = this; extra.add( component ); } - /** - * Returns whether the component has any formatting or events applied to it - * - * @return Whether any formatting or events are applied - */ - public boolean hasFormatting() - { - return color != null || bold != null - || italic != null || underlined != null - || strikethrough != null || obfuscated != null - || hoverEvent != null || clickEvent != null; - } - /** * Converts the component into a string without any formatting * @@ -378,17 +144,19 @@ void toPlainText(StringBuilder builder) public String toLegacyText() { StringBuilder builder = new StringBuilder(); - toLegacyText( builder ); + toLegacyText(builder, null); return builder.toString(); } - void toLegacyText(StringBuilder builder) + void toLegacyText(StringBuilder builder, Formatting format) { + format = Formatting.inherit(format, this); + if ( extra != null ) { for ( BaseComponent e : extra ) { - e.toLegacyText( builder ); + e.toLegacyText(builder, format); } } } diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/Formatting.java b/chat/src/main/java/net/md_5/bungee/api/chat/Formatting.java new file mode 100644 index 00000000000..2f8049294a5 --- /dev/null +++ b/chat/src/main/java/net/md_5/bungee/api/chat/Formatting.java @@ -0,0 +1,277 @@ +package net.md_5.bungee.api.chat; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import net.md_5.bungee.api.ChatColor; + +@Setter +@NoArgsConstructor +public class Formatting { + + /** + * The color of this component and any child components (unless overridden) + */ + private ChatColor color; + /** + * Whether this component and any child components (unless overridden) is + * bold + */ + private Boolean bold; + /** + * Whether this component and any child components (unless overridden) is + * italic + */ + private Boolean italic; + /** + * Whether this component and any child components (unless overridden) is + * underlined + */ + private Boolean underlined; + /** + * Whether this component and any child components (unless overridden) is + * strikethrough + */ + private Boolean strikethrough; + /** + * Whether this component and any child components (unless overridden) is + * obfuscated + */ + private Boolean obfuscated; + /** + * The action to preform when this component (and child components) are + * clicked + */ + @Getter + private ClickEvent clickEvent; + /** + * The action to preform when this component (and child components) are + * hovered over + */ + @Getter + private HoverEvent hoverEvent; + + public Formatting(Formatting parent, Formatting child) { + this.color = child.getColorRaw() != null ? child.getColorRaw() : parent.getColorRaw(); + + this.bold = child.isBoldRaw() != null ? child.isBoldRaw() : parent.isBoldRaw(); + this.italic = child.isItalicRaw() != null ? child.isItalicRaw() : parent.isItalicRaw(); + this.underlined = child.isUnderlinedRaw() != null ? child.isUnderlinedRaw() : parent.isUnderlinedRaw(); + this.strikethrough = child.isStrikethroughRaw() != null ? child.isStrikethroughRaw() : parent.isStrikethroughRaw(); + this.obfuscated = child.isObfuscatedRaw() != null ? child.isObfuscatedRaw() : parent.isObfuscatedRaw(); + + this.clickEvent = child.getClickEvent() != null ? child.getClickEvent() : parent.getClickEvent(); + this.hoverEvent = child.getHoverEvent() != null ? child.getHoverEvent() : parent.getHoverEvent(); + } + + public static Formatting inherit(Formatting parent, Formatting child) { + if(parent == null || parent == child || !parent.hasFormatting()) { + return child; + } else { + return new Formatting(parent, child); + } + } + + /** + * Returns the color of this component. This uses the parent's color if this + * component doesn't have one. {@link net.md_5.bungee.api.ChatColor#WHITE} + * is returned if no color is found. + * + * @return the color of this component + */ + public ChatColor getColor() { + return getColor(null); + } + + public ChatColor getColor(Formatting parent) + { + if ( color == null ) + { + if ( parent == null ) + { + return ChatColor.WHITE; + } + return parent.getColor(); + } + return color; + } + + /** + * Returns the color of this component without checking the parents color. + * May return null + * + * @return the color of this component + */ + public ChatColor getColorRaw() + { + return color; + } + + /** + * Returns whether this component is bold. This uses the parent's setting if + * this component hasn't been set. false is returned if none of the parent + * chain has been set. + * + * @return whether the component is bold + */ + public boolean isBold() { + return isBold(null); + } + + public boolean isBold(Formatting parent) + { + if ( bold == null ) + { + return parent != null && parent.isBold(); + } + return bold; + } + + /** + * Returns whether this component is bold without checking the parents + * setting. May return null + * + * @return whether the component is bold + */ + public Boolean isBoldRaw() + { + return bold; + } + + /** + * Returns whether this component is italic. This uses the parent's setting + * if this component hasn't been set. false is returned if none of the + * parent chain has been set. + * + * @return whether the component is italic + */ + public boolean isItalic() { + return isItalic(null); + } + + public boolean isItalic(Formatting parent) + { + if ( italic == null ) + { + return parent != null && parent.isItalic(); + } + return italic; + } + + /** + * Returns whether this component is italic without checking the parents + * setting. May return null + * + * @return whether the component is italic + */ + public Boolean isItalicRaw() + { + return italic; + } + + /** + * Returns whether this component is underlined. This uses the parent's + * setting if this component hasn't been set. false is returned if none of + * the parent chain has been set. + * + * @return whether the component is underlined + */ + public boolean isUnderlined() { + return isUnderlined(null); + } + + public boolean isUnderlined(Formatting parent) + { + if ( underlined == null ) + { + return parent != null && parent.isUnderlined(); + } + return underlined; + } + + /** + * Returns whether this component is underlined without checking the parents + * setting. May return null + * + * @return whether the component is underlined + */ + public Boolean isUnderlinedRaw() + { + return underlined; + } + + /** + * Returns whether this component is strikethrough. This uses the parent's + * setting if this component hasn't been set. false is returned if none of + * the parent chain has been set. + * + * @return whether the component is strikethrough + */ + public boolean isStrikethrough() { + return isStrikethrough(null); + } + + public boolean isStrikethrough(Formatting parent) + { + if ( strikethrough == null ) + { + return parent != null && parent.isStrikethrough(); + } + return strikethrough; + } + + /** + * Returns whether this component is strikethrough without checking the + * parents setting. May return null + * + * @return whether the component is strikethrough + */ + public Boolean isStrikethroughRaw() + { + return strikethrough; + } + + /** + * Returns whether this component is obfuscated. This uses the parent's + * setting if this component hasn't been set. false is returned if none of + * the parent chain has been set. + * + * @return whether the component is obfuscated + */ + public boolean isObfuscated() { + return isObfuscated(null); + } + + public boolean isObfuscated(Formatting parent) + { + if ( obfuscated == null ) + { + return parent != null && parent.isObfuscated(); + } + return obfuscated; + } + + /** + * Returns whether this component is obfuscated without checking the parents + * setting. May return null + * + * @return whether the component is obfuscated + */ + public Boolean isObfuscatedRaw() + { + return obfuscated; + } + + /** + * Returns whether the component has any formatting or events applied to it + * + * @return Whether any formatting or events are applied + */ + public boolean hasFormatting() + { + return color != null || bold != null + || italic != null || underlined != null + || strikethrough != null || obfuscated != null + || hoverEvent != null || clickEvent != null; + } +} diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/TextComponent.java b/chat/src/main/java/net/md_5/bungee/api/chat/TextComponent.java index 4a0be74cb39..50b8fc117a9 100644 --- a/chat/src/main/java/net/md_5/bungee/api/chat/TextComponent.java +++ b/chat/src/main/java/net/md_5/bungee/api/chat/TextComponent.java @@ -180,31 +180,33 @@ protected void toPlainText(StringBuilder builder) } @Override - protected void toLegacyText(StringBuilder builder) + protected void toLegacyText(StringBuilder builder, Formatting format) { - builder.append( getColor() ); - if ( isBold() ) + format = Formatting.inherit(format, this); + + builder.append( getColor(format) ); + if ( isBold(format) ) { builder.append( ChatColor.BOLD ); } - if ( isItalic() ) + if ( isItalic(format) ) { builder.append( ChatColor.ITALIC ); } - if ( isUnderlined() ) + if ( isUnderlined(format) ) { builder.append( ChatColor.UNDERLINE ); } - if ( isStrikethrough() ) + if ( isStrikethrough(format) ) { builder.append( ChatColor.STRIKETHROUGH ); } - if ( isObfuscated() ) + if ( isObfuscated(format) ) { builder.append( ChatColor.MAGIC ); } builder.append( text ); - super.toLegacyText( builder ); + super.toLegacyText( builder, format ); } @Override diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java b/chat/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java index 9005c7b0824..4a761c4c5ee 100644 --- a/chat/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java +++ b/chat/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java @@ -94,10 +94,6 @@ public BaseComponent duplicate() */ public void setWith(List components) { - for ( BaseComponent component : components ) - { - component.parent = this; - } with = components; } @@ -124,7 +120,6 @@ public void addWith(BaseComponent component) { with = new ArrayList(); } - component.parent = this; with.add( component ); } @@ -172,12 +167,14 @@ protected void toPlainText(StringBuilder builder) } @Override - protected void toLegacyText(StringBuilder builder) + protected void toLegacyText(StringBuilder builder, Formatting format) { + format = Formatting.inherit(format, this); + try { String trans = locales.getString( translate ); - Matcher matcher = format.matcher( trans ); + Matcher matcher = this.format.matcher(trans); int position = 0; int i = 0; while ( matcher.find( position ) ) @@ -185,7 +182,7 @@ protected void toLegacyText(StringBuilder builder) int pos = matcher.start(); if ( pos != position ) { - addFormat( builder ); + addFormat( builder, format ); builder.append( trans.substring( position, pos ) ); } position = matcher.end(); @@ -196,47 +193,47 @@ protected void toLegacyText(StringBuilder builder) case 's': case 'd': String withIndex = matcher.group( 1 ); - with.get( withIndex != null ? Integer.parseInt( withIndex ) - 1 : i++ ).toLegacyText( builder ); + with.get( withIndex != null ? Integer.parseInt( withIndex ) - 1 : i++ ).toLegacyText( builder, format ); break; case '%': - addFormat( builder ); + addFormat( builder, format); builder.append( '%' ); break; } } if ( trans.length() != position ) { - addFormat( builder ); + addFormat( builder, format ); builder.append( trans.substring( position, trans.length() ) ); } } catch ( MissingResourceException e ) { - addFormat( builder ); + addFormat( builder, format ); builder.append( translate ); } - super.toLegacyText( builder ); + super.toLegacyText( builder, format ); } - private void addFormat(StringBuilder builder) + private void addFormat(StringBuilder builder, Formatting format) { - builder.append( getColor() ); - if ( isBold() ) + builder.append( getColor(format) ); + if ( isBold(format) ) { builder.append( ChatColor.BOLD ); } - if ( isItalic() ) + if ( isItalic(format) ) { builder.append( ChatColor.ITALIC ); } - if ( isUnderlined() ) + if ( isUnderlined(format) ) { builder.append( ChatColor.UNDERLINE ); } - if ( isStrikethrough() ) + if ( isStrikethrough(format) ) { builder.append( ChatColor.STRIKETHROUGH ); } - if ( isObfuscated() ) + if ( isObfuscated(format) ) { builder.append( ChatColor.MAGIC ); }