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 17cfccd0a0..51824022bd 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/bootstrap/src/main/java/net/md_5/bungee/BungeeCordLauncher.java b/bootstrap/src/main/java/net/md_5/bungee/BungeeCordLauncher.java index 68f104b807..625a18b1d3 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 ) ); } } 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 4ae1654d2f..77752778a7 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,53 +1,20 @@ package net.md_5.bungee.api.chat; -import lombok.AccessLevel; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableSet; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import net.md_5.bungee.api.ChatColor; import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import lombok.ToString; +import java.util.Set; @Setter -@ToString(exclude = "parent") @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 @@ -55,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() ); @@ -127,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; } @@ -325,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 * @@ -374,18 +144,59 @@ 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); + } + } + } + + 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/Formatting.java b/chat/src/main/java/net/md_5/bungee/api/chat/Formatting.java new file mode 100644 index 0000000000..2f8049294a --- /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 49214f8716..50b8fc117a 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; @@ -178,36 +180,38 @@ 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 - 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 c00b7fa94a..4a761c4c5e 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 { @@ -93,10 +94,6 @@ public BaseComponent duplicate() */ public void setWith(List components) { - for ( BaseComponent component : components ) - { - component.parent = this; - } with = components; } @@ -123,7 +120,6 @@ public void addWith(BaseComponent component) { with = new ArrayList(); } - component.parent = this; with.add( component ); } @@ -171,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 ) ) @@ -184,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(); @@ -195,49 +193,64 @@ 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 ); } } + + @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 cb1d24a25d..3d3919343d 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 ) { 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 5756fc4afd..8fbd4bcfdc 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>(); 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 cd4057f960..7f1a3b1daa 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() ); } } } diff --git a/pom.xml b/pom.xml index 38975e8701..8e5872034b 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 + + 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 6c0ef4dfa0..d3ee377d8b 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) 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 128d393491..1d6568911e 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, @@ -122,12 +122,12 @@ 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) { - 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/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java index 8fc2f88263..07a707114e 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(); @@ -171,7 +159,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 { @@ -196,7 +185,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" ) ) @@ -216,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" ); } /** 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 5282033a8d..3740bf2e82 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 fb437c5766..5f0484c4d0 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() 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 56067db8cc..47f96b1cd7 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 ); } 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 06d19c3c1b..955e7c9fca 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,12 @@ 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 ex) { + // Sometimes packet-encoder is not in the pipeline, probably when the client disconnects soon after connecting + return; + } } if ( compressionThreshold != -1 ) {