From c20d8f9cd6c08c2959df562db17ab633ac93a6f2 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Fri, 6 Dec 2013 17:23:28 +0000 Subject: [PATCH 01/18] Chat Component API --- .../net/md_5/bungee/api/CommandSender.java | 16 ++ .../java/net/md_5/bungee/api/ProxyServer.java | 15 + .../md_5/bungee/api/chat/BaseComponent.java | 256 ++++++++++++++++++ .../net/md_5/bungee/api/chat/ClickEvent.java | 23 ++ .../net/md_5/bungee/api/chat/HoverEvent.java | 39 +++ .../md_5/bungee/api/chat/TextComponent.java | 43 +++ .../api/chat/TranslatableComponent.java | 53 ++++ .../main/java/net/md_5/bungee/BungeeCord.java | 21 +- .../java/net/md_5/bungee/ChatConverter.java | 197 -------------- .../java/net/md_5/bungee/UserConnection.java | 18 +- .../bungee/chat/BaseComponentSerializer.java | 95 +++++++ .../md_5/bungee/chat/ComponentSerializer.java | 147 ++++++++++ .../bungee/chat/TextComponentSerializer.java | 28 ++ .../chat/TranslatableComponentSerializer.java | 35 +++ .../bungee/command/ConsoleCommandSender.java | 15 + .../bungee/connection/InitialHandler.java | 3 +- 16 files changed, 799 insertions(+), 205 deletions(-) create mode 100644 api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java create mode 100644 api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java create mode 100644 api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java create mode 100644 api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java create mode 100644 api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java delete mode 100644 proxy/src/main/java/net/md_5/bungee/ChatConverter.java create mode 100644 proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java create mode 100644 proxy/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java create mode 100644 proxy/src/main/java/net/md_5/bungee/chat/TextComponentSerializer.java create mode 100644 proxy/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java diff --git a/api/src/main/java/net/md_5/bungee/api/CommandSender.java b/api/src/main/java/net/md_5/bungee/api/CommandSender.java index 0a51b57f..061f889f 100644 --- a/api/src/main/java/net/md_5/bungee/api/CommandSender.java +++ b/api/src/main/java/net/md_5/bungee/api/CommandSender.java @@ -1,5 +1,7 @@ package net.md_5.bungee.api; +import net.md_5.bungee.api.chat.BaseComponent; + import java.util.Collection; public interface CommandSender @@ -27,6 +29,20 @@ public interface CommandSender */ public void sendMessages(String... messages); + /** + * Send a message to this sender. + * + * @param message the message to send + */ + public void sendMessage(BaseComponent[] message); + + /** + * Send a message to this sender. + * + * @param message the message to send + */ + public void sendMessage(BaseComponent message); + /** * Get all groups this user is part of. This returns an unmodifiable * collection. diff --git a/api/src/main/java/net/md_5/bungee/api/ProxyServer.java b/api/src/main/java/net/md_5/bungee/api/ProxyServer.java index 65d20404..bc7bf2c3 100644 --- a/api/src/main/java/net/md_5/bungee/api/ProxyServer.java +++ b/api/src/main/java/net/md_5/bungee/api/ProxyServer.java @@ -1,5 +1,6 @@ package net.md_5.bungee.api; +import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.plugin.PluginManager; import com.google.common.base.Preconditions; import java.io.File; @@ -233,6 +234,20 @@ public abstract class ProxyServer */ public abstract void broadcast(String message); + /** + * Send the specified message to the console and all connected players. + * + * @param message the message to broadcast + */ + public abstract void broadcast(BaseComponent[] message); + + /** + * Send the specified message to the console and all connected players. + * + * @param message the message to broadcast + */ + public abstract void broadcast(BaseComponent message); + /** * Gets a new instance of this proxies custom tab list. * diff --git a/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java new file mode 100644 index 00000000..3a52cf13 --- /dev/null +++ b/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java @@ -0,0 +1,256 @@ +package net.md_5.bungee.api.chat; + +import lombok.AccessLevel; +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; + +@Getter +@Setter +@NoArgsConstructor +public abstract class BaseComponent { + + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + BaseComponent parent; + + //Formatting + @Getter(AccessLevel.NONE) + private ChatColor color; + @Getter(AccessLevel.NONE) + private Boolean bold; + @Getter(AccessLevel.NONE) + private Boolean italic; + @Getter(AccessLevel.NONE) + private Boolean underlined; + @Getter(AccessLevel.NONE) + private Boolean strikethrough; + @Getter(AccessLevel.NONE) + private Boolean obfuscated; + + //Appended components + private List extra; + + //Events + private ClickEvent clickEvent; + private HoverEvent hoverEvent; + + public BaseComponent(BaseComponent old) { + setColor(old.getColorRaw()); + setBold(old.isBoldRaw()); + setItalic(old.isItalicRaw()); + setUnderlined(old.isUnderlined()); + setStrikethrough(old.isStrikethroughRaw()); + setObfuscated(old.isObfuscatedRaw()); + } + + + /** + * 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; + } + + /** + * Appends a text element to the component. The text will + * inherit this component's formatting + * @param text the text to append + */ + public void addExtra(String text) { + addExtra(new TextComponent(text)); + } + + /** + * Appends a component to the component. The text will + * inherit this component's formatting + * @param component the component to append + */ + public void addExtra(BaseComponent component) { + if (extra == null) { + extra = new ArrayList<>(); + } + component.parent = this; + extra.add(component); + } + + public boolean hasFormatting() { + return color != null || bold != null || + italic != null || underlined != null || + strikethrough != null || obfuscated != null; + } + + public String toPlainText() { + StringBuilder builder = new StringBuilder(); + toPlainText(builder); + return builder.toString(); + } + + protected void toPlainText(StringBuilder builder) { + if (extra != null) { + for (BaseComponent e : extra) { + e.toPlainText(builder); + } + } + } + + public String toLegacyText() { + StringBuilder builder = new StringBuilder(); + toLegacyText(builder); + return builder.toString(); + } + + protected void toLegacyText(StringBuilder builder) { + if (extra != null) { + for (BaseComponent e : extra) { + e.toLegacyText(builder); + } + } + } + + + @Override + public String toString() { + return String.format("BaseComponent{color=%s, bold=%b, italic=%b, underlined=%b, strikethrough=%b, obfuscated=%b}", getColor().getName(), isBold(), isItalic(), isUnderlined(), isStrikethrough(), isObfuscated()); + } +} diff --git a/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java b/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java new file mode 100644 index 00000000..05ccf63a --- /dev/null +++ b/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java @@ -0,0 +1,23 @@ +package net.md_5.bungee.api.chat; + + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class ClickEvent { + private Action action; + private String value; + + public enum Action { + OPEN_URL, + OPEN_FILE, + RUN_COMMAND, + SUGGEST_COMMAND + } +} diff --git a/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java b/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java new file mode 100644 index 00000000..00283e35 --- /dev/null +++ b/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java @@ -0,0 +1,39 @@ +package net.md_5.bungee.api.chat; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@NoArgsConstructor +public class HoverEvent { + @Getter + @Setter + private Action action; + + @Getter + private Object value; + + public HoverEvent(Action action, String value) { + setAction(action); + setValue(value); + } + + public HoverEvent(Action action, BaseComponent value) { + setAction(action); + setValue(value); + } + + public void setValue(String value) { + this.value = value; + } + + public void setValue(BaseComponent value) { + this.value = value; + } + + public enum Action { + SHOW_TEXT, + SHOW_ACHIEVEMENT, + SHOW_ITEM + } +} diff --git a/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java new file mode 100644 index 00000000..6ec8da9e --- /dev/null +++ b/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java @@ -0,0 +1,43 @@ +package net.md_5.bungee.api.chat; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import net.md_5.bungee.api.ChatColor; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class TextComponent extends BaseComponent { + private String text; + + public TextComponent(TextComponent old) { + super(old); + setText(old.getText()); + } + + @Override + protected void toPlainText(StringBuilder builder) { + builder.append(text); + super.toPlainText(builder); + } + + @Override + protected void toLegacyText(StringBuilder builder) { + builder.append(getColor()); + if (isBold()) builder.append(ChatColor.BOLD); + if (isItalic()) builder.append(ChatColor.ITALIC); + if (isUnderlined()) builder.append(ChatColor.UNDERLINE); + if (isStrikethrough()) builder.append(ChatColor.STRIKETHROUGH); + if (isObfuscated()) builder.append(ChatColor.MAGIC); + builder.append(text); + super.toLegacyText(builder); + } + + @Override + public String toString() { + return String.format("TextComponent{text=%s, %s}", text, super.toString()); + } +} diff --git a/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java new file mode 100644 index 00000000..bdad73aa --- /dev/null +++ b/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java @@ -0,0 +1,53 @@ +package net.md_5.bungee.api.chat; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +@Getter +@Setter +@NoArgsConstructor +public class TranslatableComponent extends BaseComponent { + + private String translate; + private List with; + + public TranslatableComponent(String translate, Object ...with) { + setTranslate(translate); + this.with = new ArrayList<>(); + for (Object w : with) { + if (w instanceof String) { + this.with.add(new TextComponent((String) w)); + } else { + this.with.add((BaseComponent) w); + } + } + } + + public void setWith(List components) { + for (BaseComponent component : components) { + component.parent = this; + } + with = components; + } + + @Override + protected void toPlainText(StringBuilder builder) { + //TODO + super.toPlainText(builder); + } + + @Override + protected void toLegacyText(StringBuilder builder) { + //TODO + super.toLegacyText(builder); + } + + @Override + public String toString() { + return String.format("TranslatableComponent{translate=%s, with=%s, %s}", translate, with, super.toString()); + } +} 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 a1d3b127..67c8f93f 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -1,6 +1,8 @@ package net.md_5.bungee; import com.google.common.io.ByteStreams; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.chat.ComponentSerializer; import net.md_5.bungee.log.BungeeLogger; import net.md_5.bungee.reconnect.YamlReconnectHandler; import net.md_5.bungee.scheduler.BungeeScheduler; @@ -487,10 +489,23 @@ public class BungeeCord extends ProxyServer public void broadcast(String message) { getConsole().sendMessage( message ); - // TODO: Here too - for (String msg : ChatConverter.toJSONChat( message )) { - broadcast( new Chat( msg ) ); + broadcast(ComponentSerializer.fromLegacyChat(message)); + } + + @Override + public void broadcast(BaseComponent[] message) { + StringBuilder constr = new StringBuilder(); + for (BaseComponent msg : message) { + constr.append( msg.toLegacyText() ); } + getConsole().sendMessage( constr.toString() ); + broadcast( new Chat(ComponentSerializer.toString(message)) ); + } + + @Override + public void broadcast(BaseComponent message) { + getConsole().sendMessage( message.toLegacyText() ); + broadcast( new Chat(ComponentSerializer.toString(message)) ); } public void addConnection(UserConnection con) diff --git a/proxy/src/main/java/net/md_5/bungee/ChatConverter.java b/proxy/src/main/java/net/md_5/bungee/ChatConverter.java deleted file mode 100644 index 83aa21cc..00000000 --- a/proxy/src/main/java/net/md_5/bungee/ChatConverter.java +++ /dev/null @@ -1,197 +0,0 @@ -package net.md_5.bungee; - -import com.google.gson.Gson; -import com.google.gson.annotations.SerializedName; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class ChatConverter { - - private static final Gson gson = new Gson(); - private static final char COLOR_CHAR = '\u00A7'; - private static final Pattern url = Pattern.compile("^(?:(https?)://)?([-\\w_\\.]{2,}\\.[a-z]{2,4})(/\\S*)?$"); - - public static String[] toJSONChat(String txt) { - Message msg = new Message(); - ArrayList parts = new ArrayList(); - StringBuilder outBuffer = new StringBuilder("["); - StringBuilder buf = new StringBuilder(); - Matcher matcher = url.matcher(txt); - for (int i = 0; i < txt.length(); i++) { - char c = txt.charAt(i); - if (c != COLOR_CHAR) { - int pos = txt.indexOf(' ', i); - if (pos == -1) pos = txt.length(); - if (matcher.region(i, pos).find()) { //Web link handling - msg.text = buf.toString(); - buf = new StringBuilder(); - outBuffer = append(parts, outBuffer, msg); - Message old = msg; - msg = new Message(old); - msg.clickEvent = new ClickEvent(); - msg.clickEvent.action = "open_url"; - String urlString = txt.substring(i, pos); - if (urlString.startsWith("http")) { - msg.text = msg.clickEvent.value = urlString; - } else { - msg.text = urlString; - msg.clickEvent.value = "http://" + urlString; - } - outBuffer = append(parts, outBuffer, msg); - i += pos - i - 1; - msg = new Message(old); - continue; - } - buf.append(c); - continue; - } - i++; - c = txt.charAt(i); - if (c >= 'A' && c <= 'Z') { - c += 32; - } - msg.text = buf.toString(); - buf = new StringBuilder(); - outBuffer = append(parts, outBuffer, msg); - msg = new Message(msg); - switch(c) { - case 'k': - msg.obfuscated = Boolean.TRUE; - break; - case 'l': - msg.bold = Boolean.TRUE; - break; - case 'm': - msg.strikethrough = Boolean.TRUE; - break; - case 'n': - msg.underlined = Boolean.TRUE; - break; - case 'o': - msg.italic = Boolean.TRUE; - break; - default: - msg.obfuscated = null; - msg.bold = null; - msg.strikethrough = null; - msg.underlined = null; - msg.italic = null; - if (c != 'r') { - msg.color = Color.fromCode(Character.toString(c)); - } else { - msg.color = Color.WHITE; - } - break; - } - } - msg.text = buf.toString(); - append(parts, outBuffer, msg); - - parts.add(outBuffer.append("]").toString()); - String[] pArray = new String[parts.size()]; - parts.toArray(pArray); - return pArray; - } - - private static StringBuilder append(ArrayList parts, StringBuilder outBuffer, Message part) { - String p = gson.toJson(part); - if (p.length() + outBuffer.length() + 1 >= Short.MAX_VALUE - 20) { - outBuffer.append("]"); - parts.add(outBuffer.toString()); - outBuffer = new StringBuilder("["); - } - if (outBuffer.length() != 1) { - outBuffer.append(","); - } - outBuffer.append(p); - return outBuffer; - } -} - -class Message { - public String text; - - public Boolean bold; - public Boolean italic; - public Boolean underlined; - public Boolean strikethrough; - public Boolean obfuscated; - - public Color color; - - public ClickEvent clickEvent; - - public Message() { - - } - - public Message(Message old) { - this.bold = old.bold; - this.italic = old.italic; - this.underlined = old.underlined; - this.strikethrough = old.strikethrough; - this.color = old.color; - } -} - -class ClickEvent { - public String action; - public String value; -} - -enum Color { - @SerializedName("black") - BLACK("0"), - @SerializedName("dark_blue") - DARK_BLUE("1"), - @SerializedName("dark_green") - DARK_GREEN("2"), - @SerializedName("dark_aqua") - DARK_AQUA("3"), - @SerializedName("dark_red") - DARK_RED("4"), - @SerializedName("dark_purple") - DARK_PURPLE("5"), - @SerializedName("gold") - GOLD("6"), - @SerializedName("gray") - GRAY("7"), - @SerializedName("dark_gray") - DARK_GRAY("8"), - @SerializedName("blue") - BLUE("9"), - @SerializedName("green") - GREEN("a"), - @SerializedName("aqua") - AQUA("b"), - @SerializedName("red") - RED("c"), - @SerializedName("light_purple") - LIGHT_PURPLE("d"), - @SerializedName("yellow") - YELLOW("e"), - @SerializedName("white") - WHITE("f"); - - public String code; - - Color(String code) { - this.code = code; - } - - - private static HashMap codeMap = new HashMap(); - - public static Color fromCode(String code) { - return codeMap.get(code); - } - - static { - for (Color color : values()) { - codeMap.put(color.code, color); - } - } -} 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 1e4919b1..af5ee912 100644 --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java @@ -20,12 +20,14 @@ import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.Setter; import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.event.PermissionCheckEvent; import net.md_5.bungee.api.event.ServerConnectEvent; import net.md_5.bungee.api.score.Scoreboard; import net.md_5.bungee.api.tab.TabListHandler; +import net.md_5.bungee.chat.ComponentSerializer; import net.md_5.bungee.connection.InitialHandler; import net.md_5.bungee.netty.ChannelWrapper; import net.md_5.bungee.netty.HandlerBoss; @@ -256,7 +258,7 @@ public final class UserConnection implements ProxiedPlayer @Override public synchronized void disconnect(String reason) { - disconnect0( ChatConverter.toJSONChat( reason )[0] ); + disconnect0( ComponentSerializer.toString( ComponentSerializer.fromLegacyChat( reason ) ) ); } public synchronized void disconnect0(String reason) @@ -283,9 +285,7 @@ public final class UserConnection implements ProxiedPlayer @Override public void sendMessage(String message) { - for(String msg : ChatConverter.toJSONChat( message )) { - unsafe().sendPacket( new Chat( msg ) ); - } + sendMessage(ComponentSerializer.fromLegacyChat(message)); } @Override @@ -297,6 +297,16 @@ public final class UserConnection implements ProxiedPlayer } } + @Override + public void sendMessage(BaseComponent[] message) { + unsafe().sendPacket( new Chat( ComponentSerializer.toString( message ) ) ); + } + + @Override + public void sendMessage(BaseComponent message) { + unsafe().sendPacket( new Chat( ComponentSerializer.toString( message ) ) ); + } + @Override public void sendData(String channel, byte[] data) { diff --git a/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java b/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java new file mode 100644 index 00000000..60e9049a --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java @@ -0,0 +1,95 @@ +package net.md_5.bungee.chat; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.HoverEvent; + +import java.util.Arrays; + +public class BaseComponentSerializer { + + protected void deserialize(JsonObject object, BaseComponent component, JsonDeserializationContext context) { + if (object.has("color")) { + component.setColor(ChatColor.valueOf(object.get("color").getAsString().toUpperCase())); + } + if (object.has("bold")) { + component.setBold(object.get("bold").getAsBoolean()); + } + if (object.has("italic")) { + component.setItalic(object.get("italic").getAsBoolean()); + } + if (object.has("underlined")) { + component.setUnderlined(object.get("underlined").getAsBoolean()); + } + if (object.has("strikethrough")) { + component.setUnderlined(object.get("strikethrough").getAsBoolean()); + } + if (object.has("obfuscated")) { + component.setUnderlined(object.get("obfuscated").getAsBoolean()); + } + if (object.has("extra")) { + component.setExtra(Arrays.asList((BaseComponent[])context.deserialize(object.get("extra"), BaseComponent[].class))); + } + + //Events + if (object.has("clickEvent")) { + JsonObject event = object.getAsJsonObject("clickEvent"); + component.setClickEvent(new ClickEvent( + ClickEvent.Action.valueOf(event.get("action").getAsString().toUpperCase()), + event.get("value").getAsString())); + } + if (object.has("hoverEvent")) { + JsonObject event = object.getAsJsonObject("hoverEvent"); + HoverEvent hoverEvent = new HoverEvent(); + hoverEvent.setAction(HoverEvent.Action.valueOf(event.get("action").getAsString().toUpperCase())); + Object res = context.deserialize(event.get("value"), BaseComponent.class); + if (res instanceof String) { + hoverEvent.setValue((String) res); + } else { + hoverEvent.setValue((BaseComponent) res); + } + component.setHoverEvent(hoverEvent); + } + } + + protected void serialize(JsonObject object, BaseComponent component, JsonSerializationContext context) { + if (component.getColorRaw() != null) { + object.addProperty("color", component.getColorRaw().getName()); + } + if (component.isBoldRaw() != null) { + object.addProperty("bold", component.isBoldRaw()); + } + if (component.isItalicRaw() != null) { + object.addProperty("italic", component.isItalicRaw()); + } + if (component.isUnderlinedRaw() != null) { + object.addProperty("underlined", component.isUnderlinedRaw()); + } + if (component.isStrikethroughRaw() != null) { + object.addProperty("strikethrough", component.isStrikethroughRaw()); + } + if (component.isObfuscatedRaw() != null) { + object.addProperty("obfuscated", component.isObfuscatedRaw()); + } + + if (component.getExtra() != null) { + object.add("extra", context.serialize(component.getExtra())); + } + + //Events + if (component.getClickEvent() != null) { + JsonObject clickEvent = new JsonObject(); + clickEvent.addProperty("action", component.getClickEvent().getAction().toString().toLowerCase()); + clickEvent.addProperty("value", component.getClickEvent().getValue()); + } + if (component.getHoverEvent() != null) { + JsonObject clickEvent = new JsonObject(); + clickEvent.addProperty("action", component.getHoverEvent().getAction().toString().toLowerCase()); + clickEvent.add("value", context.serialize(component.getHoverEvent().getValue())); + } + } +} diff --git a/proxy/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java b/proxy/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java new file mode 100644 index 00000000..7dd90c0a --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java @@ -0,0 +1,147 @@ +package net.md_5.bungee.chat; + +import com.google.gson.*; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.chat.TranslatableComponent; + +import javax.xml.soap.Text; +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ComponentSerializer implements JsonSerializer, JsonDeserializer { + + private final static Gson gson = new GsonBuilder(). + registerTypeAdapter(BaseComponent.class, new ComponentSerializer()). + registerTypeAdapter(TextComponent.class, new TextComponentSerializer()). + registerTypeAdapter(TranslatableComponent.class, new TranslatableComponentSerializer()). + create(); + + private static final Pattern url = Pattern.compile("^(?:(https?)://)?([-\\w_\\.]{2,}\\.[a-z]{2,4})(/\\S*)?$"); + + public static BaseComponent[] parse(String json) { + if (json.startsWith("[")) { //Array + return gson.fromJson(json, BaseComponent[].class); + } + return new BaseComponent[]{gson.fromJson(json, BaseComponent.class)}; + } + + public static String toString(BaseComponent component) { + return gson.toJson(component); + } + + public static String toString(BaseComponent[] components) { + return gson.toJson(components); + } + + public static BaseComponent[] fromLegacyChat(String message) { + ArrayList components = new ArrayList<>(); + StringBuilder builder = new StringBuilder(); + TextComponent component = new TextComponent(); + Matcher matcher = url.matcher(message); + + for ( int i = 0; i < message.length(); i++ ) { + char c = message.charAt(i); + if (c == ChatColor.COLOR_CHAR) { + i++; + c = message.charAt(i); + if (c >= 'A' && c <= 'Z') { + c += 32; + } + if (builder.length() > 0) { + TextComponent old = component; + component = new TextComponent(old); + old.setText(builder.toString()); + builder = new StringBuilder(); + components.add(old); + } + ChatColor format = ChatColor.getByChar(c); + switch (format) { + case BOLD: + component.setBold(true); + break; + case ITALIC: + component.setItalic(true); + break; + case UNDERLINE: + component.setUnderlined(true); + break; + case STRIKETHROUGH: + component.setStrikethrough(true); + break; + case MAGIC: + component.setObfuscated(true); + break; + case RESET: + format = ChatColor.WHITE; + default: + component = new TextComponent(); + component.setColor(format); + break; + } + continue; + } + int pos = message.indexOf(' ', i); + if (pos == -1) pos = message.length(); + if (matcher.region(i, pos).find()) { //Web link handling + + if (builder.length() > 0) { + TextComponent old = component; + component = new TextComponent(old); + old.setText(builder.toString()); + builder = new StringBuilder(); + components.add(old); + } + + TextComponent old = component; + component = new TextComponent(old); + ClickEvent clickEvent = new ClickEvent(); + clickEvent.setAction(ClickEvent.Action.OPEN_URL); + String urlString = message.substring(i, pos); + if (urlString.startsWith("http")) { + component.setText(urlString); + clickEvent.setValue(urlString); + } else { + component.setText(urlString); + clickEvent.setValue("http://" + urlString); + } + component.setClickEvent(clickEvent); + components.add(component); + i += pos - i - 1; + component = old; + continue; + } + builder.append(c); + } + if (builder.length() > 0) { + component.setText(builder.toString()); + components.add(component); + } + return components.toArray(new BaseComponent[components.size()]); + } + + @Override + public BaseComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + if (json.isJsonPrimitive()) { + return new TextComponent(json.getAsString()); + } + JsonObject object = json.getAsJsonObject(); + if (object.has("translate")) { + return context.deserialize(json, TranslatableComponent.class); + } + return context.deserialize(json, TextComponent.class); + } + + @Override + public JsonElement serialize(BaseComponent src, Type typeOfSrc, JsonSerializationContext context) { + return context.serialize(src, src.getClass()); + } +} diff --git a/proxy/src/main/java/net/md_5/bungee/chat/TextComponentSerializer.java b/proxy/src/main/java/net/md_5/bungee/chat/TextComponentSerializer.java new file mode 100644 index 00000000..a8d758e3 --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/chat/TextComponentSerializer.java @@ -0,0 +1,28 @@ +package net.md_5.bungee.chat; + +import com.google.gson.*; +import net.md_5.bungee.api.chat.TextComponent; + +import java.lang.reflect.Type; + +public class TextComponentSerializer extends BaseComponentSerializer implements JsonSerializer , JsonDeserializer{ + @Override + public TextComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + TextComponent component = new TextComponent(); + JsonObject object = json.getAsJsonObject(); + deserialize(object, component, context); + component.setText(object.get("text").getAsString()); + return component; + } + + @Override + public JsonElement serialize(TextComponent src, Type typeOfSrc, JsonSerializationContext context) { + if (!src.hasFormatting()) { + return new JsonPrimitive(src.getText()); + } + JsonObject object = new JsonObject(); + serialize(object, src, context); + object.addProperty("text", src.getText()); + return object; + } +} diff --git a/proxy/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java b/proxy/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java new file mode 100644 index 00000000..364c286c --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java @@ -0,0 +1,35 @@ +package net.md_5.bungee.chat; + +import com.google.gson.*; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.chat.TranslatableComponent; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; + +public class TranslatableComponentSerializer extends BaseComponentSerializer implements JsonSerializer , JsonDeserializer{ + @Override + public TranslatableComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + TranslatableComponent component = new TranslatableComponent(); + JsonObject object = json.getAsJsonObject(); + deserialize(object, component, context); + component.setTranslate(object.get("translate").getAsString()); + if (object.has("with")) { + component.setWith(Arrays.asList((BaseComponent[])context.deserialize(object.get("with"), BaseComponent[].class))); + } + return component; + } + + @Override + public JsonElement serialize(TranslatableComponent src, Type typeOfSrc, JsonSerializationContext context) { + JsonObject object = new JsonObject(); + serialize(object, src, context); + object.addProperty("translate", src.getTranslate()); + if (src.getWith() != null) { + object.add("with", context.serialize(src.getWith())); + } + return object; + } +} diff --git a/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java b/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java index 65a2505a..3cce5a38 100644 --- a/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java +++ b/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java @@ -5,6 +5,7 @@ import java.util.Collections; import lombok.Getter; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.chat.BaseComponent; /** * Command sender representing the proxy console. @@ -34,6 +35,20 @@ public class ConsoleCommandSender implements CommandSender } } + @Override + public void sendMessage(BaseComponent[] message) { + StringBuilder constr = new StringBuilder(); + for (BaseComponent msg : message) { + constr.append( msg.toLegacyText() ); + } + sendMessage( constr.toString() ); + } + + @Override + public void sendMessage(BaseComponent message) { + sendMessage( message.toLegacyText() ); + } + @Override public String getName() { diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java index 05ab8e98..a5cb8bb2 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java @@ -23,6 +23,7 @@ import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.event.LoginEvent; import net.md_5.bungee.api.event.PostLoginEvent; import net.md_5.bungee.api.event.ProxyPingEvent; +import net.md_5.bungee.chat.ComponentSerializer; import net.md_5.bungee.http.HttpClient; import net.md_5.bungee.netty.HandlerBoss; import net.md_5.bungee.netty.ChannelWrapper; @@ -397,7 +398,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection { if ( !ch.isClosed() ) { - unsafe().sendPacket( new Kick( ChatConverter.toJSONChat( reason )[0] ) ); + unsafe().sendPacket( new Kick( ComponentSerializer.toString(ComponentSerializer.fromLegacyChat(reason)) ) ); ch.close(); } } From 2c8b15cb1ef865058badc018cfeb124ab98fdf85 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Fri, 6 Dec 2013 18:26:22 +0000 Subject: [PATCH 02/18] Use components for ServerKickEvent (fixes #744) + minor refactoring --- .../net/md_5/bungee/api/CommandSender.java | 2 + .../java/net/md_5/bungee/api/ProxyServer.java | 1 + .../md_5/bungee/api/chat/BaseComponent.java | 16 ++++ .../md_5/bungee/api/chat/TextComponent.java | 93 +++++++++++++++++++ .../bungee/api/connection/Connection.java | 23 +++++ .../bungee/api/event/ServerKickEvent.java | 22 ++++- .../main/java/net/md_5/bungee/BungeeCord.java | 11 +-- .../net/md_5/bungee/ServerConnection.java | 16 +++- .../java/net/md_5/bungee/ServerConnector.java | 3 +- .../java/net/md_5/bungee/UserConnection.java | 21 ++++- .../md_5/bungee/chat/ComponentSerializer.java | 88 ------------------ .../bungee/command/ConsoleCommandSender.java | 6 +- .../bungee/connection/DownstreamBridge.java | 5 +- .../bungee/connection/InitialHandler.java | 18 +++- 14 files changed, 210 insertions(+), 115 deletions(-) diff --git a/api/src/main/java/net/md_5/bungee/api/CommandSender.java b/api/src/main/java/net/md_5/bungee/api/CommandSender.java index 061f889f..96248935 100644 --- a/api/src/main/java/net/md_5/bungee/api/CommandSender.java +++ b/api/src/main/java/net/md_5/bungee/api/CommandSender.java @@ -19,6 +19,7 @@ public interface CommandSender * * @param message the message to send */ + @Deprecated public void sendMessage(String message); /** @@ -27,6 +28,7 @@ public interface CommandSender * * @param messages the messages to send */ + @Deprecated public void sendMessages(String... messages); /** diff --git a/api/src/main/java/net/md_5/bungee/api/ProxyServer.java b/api/src/main/java/net/md_5/bungee/api/ProxyServer.java index bc7bf2c3..06155010 100644 --- a/api/src/main/java/net/md_5/bungee/api/ProxyServer.java +++ b/api/src/main/java/net/md_5/bungee/api/ProxyServer.java @@ -232,6 +232,7 @@ public abstract class ProxyServer * * @param message the message to broadcast */ + @Deprecated public abstract void broadcast(String message); /** diff --git a/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java index 3a52cf13..8651071e 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java @@ -49,6 +49,22 @@ public abstract class BaseComponent { setObfuscated(old.isObfuscatedRaw()); } + public static String toLegacyText(BaseComponent[] components) { + StringBuilder builder = new StringBuilder(); + for (BaseComponent msg : components) { + builder.append(msg.toLegacyText()); + } + return builder.toString(); + } + + public static String toPlainText(BaseComponent[] components) { + StringBuilder builder = new StringBuilder(); + for (BaseComponent msg : components) { + builder.append(msg.toPlainText()); + } + return builder.toString(); + } + /** * Returns the color of this component. This uses the parent's color diff --git a/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java index 6ec8da9e..d398f7b3 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java @@ -6,11 +6,104 @@ import lombok.NoArgsConstructor; import lombok.Setter; import net.md_5.bungee.api.ChatColor; +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + @Getter @Setter @AllArgsConstructor @NoArgsConstructor public class TextComponent extends BaseComponent { + + private static final Pattern url = Pattern.compile("^(?:(https?)://)?([-\\w_\\.]{2,}\\.[a-z]{2,4})(/\\S*)?$"); + + public static BaseComponent[] fromLegacyText(String message) { + ArrayList components = new ArrayList<>(); + StringBuilder builder = new StringBuilder(); + TextComponent component = new TextComponent(); + Matcher matcher = url.matcher(message); + + for ( int i = 0; i < message.length(); i++ ) { + char c = message.charAt(i); + if (c == ChatColor.COLOR_CHAR) { + i++; + c = message.charAt(i); + if (c >= 'A' && c <= 'Z') { + c += 32; + } + if (builder.length() > 0) { + TextComponent old = component; + component = new TextComponent(old); + old.setText(builder.toString()); + builder = new StringBuilder(); + components.add(old); + } + ChatColor format = ChatColor.getByChar(c); + switch (format) { + case BOLD: + component.setBold(true); + break; + case ITALIC: + component.setItalic(true); + break; + case UNDERLINE: + component.setUnderlined(true); + break; + case STRIKETHROUGH: + component.setStrikethrough(true); + break; + case MAGIC: + component.setObfuscated(true); + break; + case RESET: + format = ChatColor.WHITE; + default: + component = new TextComponent(); + component.setColor(format); + break; + } + continue; + } + int pos = message.indexOf(' ', i); + if (pos == -1) pos = message.length(); + if (matcher.region(i, pos).find()) { //Web link handling + + if (builder.length() > 0) { + TextComponent old = component; + component = new TextComponent(old); + old.setText(builder.toString()); + builder = new StringBuilder(); + components.add(old); + } + + TextComponent old = component; + component = new TextComponent(old); + ClickEvent clickEvent = new ClickEvent(); + clickEvent.setAction(ClickEvent.Action.OPEN_URL); + String urlString = message.substring(i, pos); + if (urlString.startsWith("http")) { + component.setText(urlString); + clickEvent.setValue(urlString); + } else { + component.setText(urlString); + clickEvent.setValue("http://" + urlString); + } + component.setClickEvent(clickEvent); + components.add(component); + i += pos - i - 1; + component = old; + continue; + } + builder.append(c); + } + if (builder.length() > 0) { + component.setText(builder.toString()); + components.add(component); + } + return components.toArray(new BaseComponent[components.size()]); + } + private String text; public TextComponent(TextComponent old) { diff --git a/api/src/main/java/net/md_5/bungee/api/connection/Connection.java b/api/src/main/java/net/md_5/bungee/api/connection/Connection.java index 2c9cae14..fb146164 100644 --- a/api/src/main/java/net/md_5/bungee/api/connection/Connection.java +++ b/api/src/main/java/net/md_5/bungee/api/connection/Connection.java @@ -1,6 +1,8 @@ package net.md_5.bungee.api.connection; import java.net.InetSocketAddress; + +import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.protocol.DefinedPacket; /** @@ -26,8 +28,29 @@ public interface Connection * @param reason the reason shown to the player / sent to the server on * disconnect */ + @Deprecated void disconnect(String reason); + /** + * Disconnects this end of the connection for the specified reason. If this + * is an {@link ProxiedPlayer} the respective server connection will be + * closed too. + * + * @param reason the reason shown to the player / sent to the server on + * disconnect + */ + void disconnect(BaseComponent[] reason); + + /** + * Disconnects this end of the connection for the specified reason. If this + * is an {@link ProxiedPlayer} the respective server connection will be + * closed too. + * + * @param reason the reason shown to the player / sent to the server on + * disconnect + */ + void disconnect(BaseComponent reason); + /** * Get the unsafe methods of this class. * diff --git a/api/src/main/java/net/md_5/bungee/api/event/ServerKickEvent.java b/api/src/main/java/net/md_5/bungee/api/event/ServerKickEvent.java index 50a4cc93..7df8d969 100644 --- a/api/src/main/java/net/md_5/bungee/api/event/ServerKickEvent.java +++ b/api/src/main/java/net/md_5/bungee/api/event/ServerKickEvent.java @@ -3,6 +3,8 @@ package net.md_5.bungee.api.event; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.plugin.Cancellable; @@ -28,7 +30,7 @@ public class ServerKickEvent extends Event implements Cancellable /** * Kick reason. */ - private String kickReason; + private BaseComponent[] kickReasonComponent; /** * Server to send player to if this event is cancelled. */ @@ -44,16 +46,26 @@ public class ServerKickEvent extends Event implements Cancellable CONNECTING, CONNECTED, UNKNOWN; } - public ServerKickEvent(ProxiedPlayer player, String kickReason, ServerInfo cancelServer) + public ServerKickEvent(ProxiedPlayer player, BaseComponent[] kickReasonComponent, ServerInfo cancelServer) { - this( player, kickReason, cancelServer, State.UNKNOWN ); + this( player, kickReasonComponent, cancelServer, State.UNKNOWN ); } - public ServerKickEvent(ProxiedPlayer player, String kickReason, ServerInfo cancelServer, State state) + public ServerKickEvent(ProxiedPlayer player, BaseComponent[] kickReasonComponent, ServerInfo cancelServer, State state) { this.player = player; - this.kickReason = kickReason; + this.kickReasonComponent = kickReasonComponent; this.cancelServer = cancelServer; this.state = state; } + + @Deprecated + public String getKickReason() { + return BaseComponent.toLegacyText(kickReasonComponent); + } + + @Deprecated + public void setKickReason(String reason) { + kickReasonComponent = TextComponent.fromLegacyText(reason); + } } 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 67c8f93f..80ca6ee9 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -2,6 +2,7 @@ package net.md_5.bungee; import com.google.common.io.ByteStreams; import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.chat.ComponentSerializer; import net.md_5.bungee.log.BungeeLogger; import net.md_5.bungee.reconnect.YamlReconnectHandler; @@ -489,17 +490,13 @@ public class BungeeCord extends ProxyServer public void broadcast(String message) { getConsole().sendMessage( message ); - broadcast(ComponentSerializer.fromLegacyChat(message)); + broadcast(TextComponent.fromLegacyText(message)); } @Override public void broadcast(BaseComponent[] message) { - StringBuilder constr = new StringBuilder(); - for (BaseComponent msg : message) { - constr.append( msg.toLegacyText() ); - } - getConsole().sendMessage( constr.toString() ); - broadcast( new Chat(ComponentSerializer.toString(message)) ); + getConsole().sendMessage(BaseComponent.toLegacyText(message)); + broadcast(new Chat(ComponentSerializer.toString(message))); } @Override diff --git a/proxy/src/main/java/net/md_5/bungee/ServerConnection.java b/proxy/src/main/java/net/md_5/bungee/ServerConnection.java index b5c5af50..f1490c64 100644 --- a/proxy/src/main/java/net/md_5/bungee/ServerConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/ServerConnection.java @@ -5,7 +5,10 @@ import java.util.concurrent.TimeUnit; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.connection.Server; +import net.md_5.bungee.chat.ComponentSerializer; import net.md_5.bungee.netty.ChannelWrapper; import net.md_5.bungee.protocol.DefinedPacket; import net.md_5.bungee.protocol.packet.PluginMessage; @@ -40,10 +43,15 @@ public class ServerConnection implements Server @Override public synchronized void disconnect(String reason) { + disconnect( TextComponent.fromLegacyText(reason) ); + } + + @Override + public void disconnect(BaseComponent[] reason) { if ( !ch.isClosed() ) { // TODO: Can we just use a future here? - unsafe().sendPacket( new Kick( reason ) ); + unsafe().sendPacket( new Kick(ComponentSerializer.toString(reason) ) ); ch.getHandle().eventLoop().schedule( new Runnable() { @Override @@ -53,6 +61,12 @@ public class ServerConnection implements Server } }, 100, TimeUnit.MILLISECONDS ); } + + } + + @Override + public void disconnect(BaseComponent reason) { + disconnect(new BaseComponent[]{reason}); } @Override 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 f3c3e93f..0d317c93 100644 --- a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java +++ b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java @@ -15,6 +15,7 @@ import net.md_5.bungee.api.event.ServerSwitchEvent; import net.md_5.bungee.api.score.Objective; import net.md_5.bungee.api.score.Scoreboard; import net.md_5.bungee.api.score.Team; +import net.md_5.bungee.chat.ComponentSerializer; import net.md_5.bungee.connection.CancelSendSignal; import net.md_5.bungee.connection.DownstreamBridge; import net.md_5.bungee.netty.HandlerBoss; @@ -211,7 +212,7 @@ public class ServerConnector extends PacketHandler { def = null; } - ServerKickEvent event = bungee.getPluginManager().callEvent( new ServerKickEvent( user, kick.getMessage(), def, ServerKickEvent.State.CONNECTING ) ); + ServerKickEvent event = bungee.getPluginManager().callEvent( new ServerKickEvent( user, ComponentSerializer.parse(kick.getMessage()), def, ServerKickEvent.State.CONNECTING ) ); if ( event.isCancelled() && event.getCancelServer() != null ) { user.connect( event.getCancelServer() ); 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 af5ee912..85d3adb1 100644 --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java @@ -21,6 +21,7 @@ import lombok.RequiredArgsConstructor; import lombok.Setter; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.event.PermissionCheckEvent; @@ -258,15 +259,25 @@ public final class UserConnection implements ProxiedPlayer @Override public synchronized void disconnect(String reason) { - disconnect0( ComponentSerializer.toString( ComponentSerializer.fromLegacyChat( reason ) ) ); + disconnect0( TextComponent.fromLegacyText(reason) ); } - public synchronized void disconnect0(String reason) + @Override + public void disconnect(BaseComponent[] reason) { + disconnect0( reason ); + } + + @Override + public void disconnect(BaseComponent reason) { + disconnect0(new BaseComponent[]{reason}); + } + + public synchronized void disconnect0(BaseComponent[] reason) { if ( ch.getHandle().isActive() ) { - bungee.getLogger().log( Level.INFO, "[" + getName() + "] disconnected with: " + reason ); - unsafe().sendPacket( new Kick( reason ) ); + bungee.getLogger().log( Level.INFO, "[" + getName() + "] disconnected with: " + BaseComponent.toLegacyText(reason) ); + unsafe().sendPacket( new Kick( ComponentSerializer.toString(reason) ) ); ch.close(); if ( server != null ) { @@ -285,7 +296,7 @@ public final class UserConnection implements ProxiedPlayer @Override public void sendMessage(String message) { - sendMessage(ComponentSerializer.fromLegacyChat(message)); + sendMessage(TextComponent.fromLegacyText(message)); } @Override diff --git a/proxy/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java b/proxy/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java index 7dd90c0a..3dc4fe79 100644 --- a/proxy/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java +++ b/proxy/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java @@ -25,8 +25,6 @@ public class ComponentSerializer implements JsonSerializer, JsonD registerTypeAdapter(TranslatableComponent.class, new TranslatableComponentSerializer()). create(); - private static final Pattern url = Pattern.compile("^(?:(https?)://)?([-\\w_\\.]{2,}\\.[a-z]{2,4})(/\\S*)?$"); - public static BaseComponent[] parse(String json) { if (json.startsWith("[")) { //Array return gson.fromJson(json, BaseComponent[].class); @@ -42,92 +40,6 @@ public class ComponentSerializer implements JsonSerializer, JsonD return gson.toJson(components); } - public static BaseComponent[] fromLegacyChat(String message) { - ArrayList components = new ArrayList<>(); - StringBuilder builder = new StringBuilder(); - TextComponent component = new TextComponent(); - Matcher matcher = url.matcher(message); - - for ( int i = 0; i < message.length(); i++ ) { - char c = message.charAt(i); - if (c == ChatColor.COLOR_CHAR) { - i++; - c = message.charAt(i); - if (c >= 'A' && c <= 'Z') { - c += 32; - } - if (builder.length() > 0) { - TextComponent old = component; - component = new TextComponent(old); - old.setText(builder.toString()); - builder = new StringBuilder(); - components.add(old); - } - ChatColor format = ChatColor.getByChar(c); - switch (format) { - case BOLD: - component.setBold(true); - break; - case ITALIC: - component.setItalic(true); - break; - case UNDERLINE: - component.setUnderlined(true); - break; - case STRIKETHROUGH: - component.setStrikethrough(true); - break; - case MAGIC: - component.setObfuscated(true); - break; - case RESET: - format = ChatColor.WHITE; - default: - component = new TextComponent(); - component.setColor(format); - break; - } - continue; - } - int pos = message.indexOf(' ', i); - if (pos == -1) pos = message.length(); - if (matcher.region(i, pos).find()) { //Web link handling - - if (builder.length() > 0) { - TextComponent old = component; - component = new TextComponent(old); - old.setText(builder.toString()); - builder = new StringBuilder(); - components.add(old); - } - - TextComponent old = component; - component = new TextComponent(old); - ClickEvent clickEvent = new ClickEvent(); - clickEvent.setAction(ClickEvent.Action.OPEN_URL); - String urlString = message.substring(i, pos); - if (urlString.startsWith("http")) { - component.setText(urlString); - clickEvent.setValue(urlString); - } else { - component.setText(urlString); - clickEvent.setValue("http://" + urlString); - } - component.setClickEvent(clickEvent); - components.add(component); - i += pos - i - 1; - component = old; - continue; - } - builder.append(c); - } - if (builder.length() > 0) { - component.setText(builder.toString()); - components.add(component); - } - return components.toArray(new BaseComponent[components.size()]); - } - @Override public BaseComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { if (json.isJsonPrimitive()) { diff --git a/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java b/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java index 3cce5a38..42a11eb3 100644 --- a/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java +++ b/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java @@ -37,11 +37,7 @@ public class ConsoleCommandSender implements CommandSender @Override public void sendMessage(BaseComponent[] message) { - StringBuilder constr = new StringBuilder(); - for (BaseComponent msg : message) { - constr.append( msg.toLegacyText() ); - } - sendMessage( constr.toString() ); + sendMessage(BaseComponent.toLegacyText(message)); } @Override diff --git a/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java index 3ab31530..93c6f325 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java @@ -22,6 +22,7 @@ import net.md_5.bungee.api.score.Position; import net.md_5.bungee.api.score.Score; import net.md_5.bungee.api.score.Scoreboard; import net.md_5.bungee.api.score.Team; +import net.md_5.bungee.chat.ComponentSerializer; import net.md_5.bungee.netty.ChannelWrapper; import net.md_5.bungee.netty.PacketHandler; import net.md_5.bungee.protocol.PacketWrapper; @@ -357,13 +358,13 @@ public class DownstreamBridge extends PacketHandler { def = null; } - ServerKickEvent event = bungee.getPluginManager().callEvent( new ServerKickEvent( con, kick.getMessage(), def, ServerKickEvent.State.CONNECTED ) ); + ServerKickEvent event = bungee.getPluginManager().callEvent( new ServerKickEvent( con, ComponentSerializer.parse(kick.getMessage()), def, ServerKickEvent.State.CONNECTED ) ); if ( event.isCancelled() && event.getCancelServer() != null ) { con.connectNow( event.getCancelServer() ); } else { - con.disconnect0( event.getKickReason() ); // TODO: Json concat util method // TODO: Prefix our own stuff. + con.disconnect0( event.getKickReasonComponent() ); // TODO: Prefix our own stuff. } server.setObsolete( true ); throw new CancelSendSignal(); diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java index a5cb8bb2..52179ea5 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java @@ -16,6 +16,8 @@ import net.md_5.bungee.api.Callback; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ServerPing; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.config.ListenerInfo; import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.PendingConnection; @@ -398,11 +400,25 @@ public class InitialHandler extends PacketHandler implements PendingConnection { if ( !ch.isClosed() ) { - unsafe().sendPacket( new Kick( ComponentSerializer.toString(ComponentSerializer.fromLegacyChat(reason)) ) ); + unsafe().sendPacket( new Kick( ComponentSerializer.toString(TextComponent.fromLegacyText(reason)) ) ); ch.close(); } } + @Override + public void disconnect(BaseComponent[] reason) { + if ( !ch.isClosed() ) + { + unsafe().sendPacket( new Kick( ComponentSerializer.toString(reason) ) ); + ch.close(); + } + } + + @Override + public void disconnect(BaseComponent reason) { + disconnect(new BaseComponent[]{reason}); + } + @Override public String getName() { From 35c1b26a20c0ea757b4a6754436c4e2e6c816fdb Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Fri, 6 Dec 2013 18:45:37 +0000 Subject: [PATCH 03/18] Fix formatting --- .../md_5/bungee/api/chat/BaseComponent.java | 165 ++++++++++++------ .../net/md_5/bungee/api/chat/ClickEvent.java | 6 +- .../net/md_5/bungee/api/chat/HoverEvent.java | 26 +-- .../md_5/bungee/api/chat/TextComponent.java | 136 ++++++++------- .../api/chat/TranslatableComponent.java | 42 +++-- .../bungee/api/event/ServerKickEvent.java | 4 +- .../main/java/net/md_5/bungee/BungeeCord.java | 14 +- .../net/md_5/bungee/ServerConnection.java | 14 +- .../java/net/md_5/bungee/UserConnection.java | 28 +-- .../bungee/chat/BaseComponentSerializer.java | 125 +++++++------ .../md_5/bungee/chat/ComponentSerializer.java | 69 ++++---- .../bungee/chat/TextComponentSerializer.java | 31 ++-- .../chat/TranslatableComponentSerializer.java | 37 ++-- .../bungee/command/ConsoleCommandSender.java | 10 +- .../bungee/connection/InitialHandler.java | 16 +- 15 files changed, 436 insertions(+), 287 deletions(-) diff --git a/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java index 8651071e..53eae97d 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java @@ -7,13 +7,13 @@ import lombok.Setter; import net.md_5.bungee.api.ChatColor; import java.util.ArrayList; -import java.util.Collections; import java.util.List; @Getter @Setter @NoArgsConstructor -public abstract class BaseComponent { +public abstract class BaseComponent +{ @Getter(AccessLevel.NONE) @Setter(AccessLevel.NONE) @@ -40,27 +40,32 @@ public abstract class BaseComponent { private ClickEvent clickEvent; private HoverEvent hoverEvent; - public BaseComponent(BaseComponent old) { - setColor(old.getColorRaw()); - setBold(old.isBoldRaw()); - setItalic(old.isItalicRaw()); - setUnderlined(old.isUnderlined()); - setStrikethrough(old.isStrikethroughRaw()); - setObfuscated(old.isObfuscatedRaw()); + public BaseComponent(BaseComponent old) + { + setColor( old.getColorRaw() ); + setBold( old.isBoldRaw() ); + setItalic( old.isItalicRaw() ); + setUnderlined( old.isUnderlined() ); + setStrikethrough( old.isStrikethroughRaw() ); + setObfuscated( old.isObfuscatedRaw() ); } - public static String toLegacyText(BaseComponent[] components) { + public static String toLegacyText(BaseComponent[] components) + { StringBuilder builder = new StringBuilder(); - for (BaseComponent msg : components) { - builder.append(msg.toLegacyText()); + for (BaseComponent msg : components) + { + builder.append( msg.toLegacyText() ); } return builder.toString(); } - public static String toPlainText(BaseComponent[] components) { + public static String toPlainText(BaseComponent[] components) + { StringBuilder builder = new StringBuilder(); - for (BaseComponent msg : components) { - builder.append(msg.toPlainText()); + for (BaseComponent msg : components) + { + builder.append( msg.toPlainText() ); } return builder.toString(); } @@ -70,11 +75,15 @@ public abstract class BaseComponent { * 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) { + public ChatColor getColor() + { + if (color == null) + { + if (parent == null) + { return ChatColor.WHITE; } return parent.getColor(); @@ -85,9 +94,11 @@ public abstract class BaseComponent { /** * Returns the color of this component without checking the parents * color. May return null + * * @return the color of this component */ - public ChatColor getColorRaw() { + public ChatColor getColorRaw() + { return color; } @@ -95,10 +106,13 @@ public abstract class BaseComponent { * 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) { + public boolean isBold() + { + if (bold == null) + { return parent != null && parent.isBold(); } return bold; @@ -107,9 +121,11 @@ public abstract class BaseComponent { /** * Returns whether this component is bold without checking * the parents setting. May return null + * * @return whether the component is bold */ - public Boolean isBoldRaw() { + public Boolean isBoldRaw() + { return bold; } @@ -117,10 +133,13 @@ public abstract class BaseComponent { * 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) { + public boolean isItalic() + { + if (italic == null) + { return parent != null && parent.isItalic(); } return italic; @@ -129,9 +148,11 @@ public abstract class BaseComponent { /** * Returns whether this component is italic without checking * the parents setting. May return null + * * @return whether the component is italic */ - public Boolean isItalicRaw() { + public Boolean isItalicRaw() + { return italic; } @@ -139,10 +160,13 @@ public abstract class BaseComponent { * 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) { + public boolean isUnderlined() + { + if (underlined == null) + { return parent != null && parent.isUnderlined(); } return underlined; @@ -151,9 +175,11 @@ public abstract class BaseComponent { /** * Returns whether this component is underlined without checking * the parents setting. May return null + * * @return whether the component is underlined */ - public Boolean isUnderlinedRaw() { + public Boolean isUnderlinedRaw() + { return underlined; } @@ -161,10 +187,13 @@ public abstract class BaseComponent { * 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) { + public boolean isStrikethrough() + { + if (strikethrough == null) + { return parent != null && parent.isStrikethrough(); } return strikethrough; @@ -173,9 +202,11 @@ public abstract class BaseComponent { /** * Returns whether this component is strikethrough without checking * the parents setting. May return null + * * @return whether the component is strikethrough */ - public Boolean isStrikethroughRaw() { + public Boolean isStrikethroughRaw() + { return strikethrough; } @@ -183,10 +214,13 @@ public abstract class BaseComponent { * 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) { + public boolean isObfuscated() + { + if (obfuscated == null) + { return parent != null && parent.isObfuscated(); } return obfuscated; @@ -195,14 +229,18 @@ public abstract class BaseComponent { /** * Returns whether this component is obfuscated without checking * the parents setting. May return null + * * @return whether the component is obfuscated */ - public Boolean isObfuscatedRaw() { + public Boolean isObfuscatedRaw() + { return obfuscated; } - public void setExtra(List components) { - for (BaseComponent component : components) { + public void setExtra(List components) + { + for (BaseComponent component : components) + { component.parent = this; } extra = components; @@ -211,62 +249,77 @@ public abstract class BaseComponent { /** * Appends a text element to the component. The text will * inherit this component's formatting + * * @param text the text to append */ - public void addExtra(String text) { - addExtra(new TextComponent(text)); + public void addExtra(String text) + { + addExtra( new TextComponent( text ) ); } /** * Appends a component to the component. The text will * inherit this component's formatting + * * @param component the component to append */ - public void addExtra(BaseComponent component) { - if (extra == null) { + public void addExtra(BaseComponent component) + { + if (extra == null) + { extra = new ArrayList<>(); } component.parent = this; - extra.add(component); + extra.add( component ); } - public boolean hasFormatting() { + public boolean hasFormatting() + { return color != null || bold != null || italic != null || underlined != null || strikethrough != null || obfuscated != null; } - public String toPlainText() { + public String toPlainText() + { StringBuilder builder = new StringBuilder(); - toPlainText(builder); + toPlainText( builder ); return builder.toString(); } - protected void toPlainText(StringBuilder builder) { - if (extra != null) { - for (BaseComponent e : extra) { - e.toPlainText(builder); + protected void toPlainText(StringBuilder builder) + { + if (extra != null) + { + for (BaseComponent e : extra) + { + e.toPlainText( builder ); } } } - public String toLegacyText() { + public String toLegacyText() + { StringBuilder builder = new StringBuilder(); - toLegacyText(builder); + toLegacyText( builder ); return builder.toString(); } - protected void toLegacyText(StringBuilder builder) { - if (extra != null) { - for (BaseComponent e : extra) { - e.toLegacyText(builder); + protected void toLegacyText(StringBuilder builder) + { + if (extra != null) + { + for (BaseComponent e : extra) + { + e.toLegacyText( builder ); } } } @Override - public String toString() { - return String.format("BaseComponent{color=%s, bold=%b, italic=%b, underlined=%b, strikethrough=%b, obfuscated=%b}", getColor().getName(), isBold(), isItalic(), isUnderlined(), isStrikethrough(), isObfuscated()); + public String toString() + { + return String.format( "BaseComponent{color=%s, bold=%b, italic=%b, underlined=%b, strikethrough=%b, obfuscated=%b}", getColor().getName(), isBold(), isItalic(), isUnderlined(), isStrikethrough(), isObfuscated() ); } } diff --git a/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java b/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java index 05ccf63a..3d0dd90b 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java @@ -10,11 +10,13 @@ import lombok.Setter; @Setter @AllArgsConstructor @NoArgsConstructor -public class ClickEvent { +public class ClickEvent +{ private Action action; private String value; - public enum Action { + public enum Action + { OPEN_URL, OPEN_FILE, RUN_COMMAND, diff --git a/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java b/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java index 00283e35..8252a954 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java @@ -5,7 +5,8 @@ import lombok.NoArgsConstructor; import lombok.Setter; @NoArgsConstructor -public class HoverEvent { +public class HoverEvent +{ @Getter @Setter private Action action; @@ -13,25 +14,30 @@ public class HoverEvent { @Getter private Object value; - public HoverEvent(Action action, String value) { - setAction(action); - setValue(value); + public HoverEvent(Action action, String value) + { + setAction( action ); + setValue( value ); } - public HoverEvent(Action action, BaseComponent value) { - setAction(action); - setValue(value); + public HoverEvent(Action action, BaseComponent value) + { + setAction( action ); + setValue( value ); } - public void setValue(String value) { + public void setValue(String value) + { this.value = value; } - public void setValue(BaseComponent value) { + public void setValue(BaseComponent value) + { this.value = value; } - public enum Action { + public enum Action + { SHOW_TEXT, SHOW_ACHIEVEMENT, SHOW_ITEM diff --git a/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java index d398f7b3..723183f2 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java @@ -14,123 +14,139 @@ import java.util.regex.Pattern; @Setter @AllArgsConstructor @NoArgsConstructor -public class TextComponent extends BaseComponent { +public class TextComponent extends BaseComponent +{ - private static final Pattern url = Pattern.compile("^(?:(https?)://)?([-\\w_\\.]{2,}\\.[a-z]{2,4})(/\\S*)?$"); + private static final Pattern url = Pattern.compile( "^(?:(https?)://)?([-\\w_\\.]{2,}\\.[a-z]{2,4})(/\\S*)?$" ); - public static BaseComponent[] fromLegacyText(String message) { + public static BaseComponent[] fromLegacyText(String message) + { ArrayList components = new ArrayList<>(); StringBuilder builder = new StringBuilder(); TextComponent component = new TextComponent(); - Matcher matcher = url.matcher(message); + Matcher matcher = url.matcher( message ); - for ( int i = 0; i < message.length(); i++ ) { - char c = message.charAt(i); - if (c == ChatColor.COLOR_CHAR) { + for (int i = 0; i < message.length(); i++) + { + char c = message.charAt( i ); + if (c == ChatColor.COLOR_CHAR) + { i++; - c = message.charAt(i); - if (c >= 'A' && c <= 'Z') { + c = message.charAt( i ); + if (c >= 'A' && c <= 'Z') + { c += 32; } - if (builder.length() > 0) { + if (builder.length() > 0) + { TextComponent old = component; - component = new TextComponent(old); - old.setText(builder.toString()); + component = new TextComponent( old ); + old.setText( builder.toString() ); builder = new StringBuilder(); - components.add(old); + components.add( old ); } - ChatColor format = ChatColor.getByChar(c); - switch (format) { + ChatColor format = ChatColor.getByChar( c ); + switch (format) + { case BOLD: - component.setBold(true); + component.setBold( true ); break; case ITALIC: - component.setItalic(true); + component.setItalic( true ); break; case UNDERLINE: - component.setUnderlined(true); + component.setUnderlined( true ); break; case STRIKETHROUGH: - component.setStrikethrough(true); + component.setStrikethrough( true ); break; case MAGIC: - component.setObfuscated(true); + component.setObfuscated( true ); break; case RESET: format = ChatColor.WHITE; default: component = new TextComponent(); - component.setColor(format); + component.setColor( format ); break; } continue; } - int pos = message.indexOf(' ', i); + int pos = message.indexOf( ' ', i ); if (pos == -1) pos = message.length(); - if (matcher.region(i, pos).find()) { //Web link handling + if (matcher.region( i, pos ).find()) + { //Web link handling - if (builder.length() > 0) { + if (builder.length() > 0) + { TextComponent old = component; - component = new TextComponent(old); - old.setText(builder.toString()); + component = new TextComponent( old ); + old.setText( builder.toString() ); builder = new StringBuilder(); - components.add(old); + components.add( old ); } TextComponent old = component; - component = new TextComponent(old); + component = new TextComponent( old ); ClickEvent clickEvent = new ClickEvent(); - clickEvent.setAction(ClickEvent.Action.OPEN_URL); - String urlString = message.substring(i, pos); - if (urlString.startsWith("http")) { - component.setText(urlString); - clickEvent.setValue(urlString); - } else { - component.setText(urlString); - clickEvent.setValue("http://" + urlString); + clickEvent.setAction( ClickEvent.Action.OPEN_URL ); + String urlString = message.substring( i, pos ); + if (urlString.startsWith( "http" )) + { + component.setText( urlString ); + clickEvent.setValue( urlString ); + } else + { + component.setText( urlString ); + clickEvent.setValue( "http://" + urlString ); } - component.setClickEvent(clickEvent); - components.add(component); + component.setClickEvent( clickEvent ); + components.add( component ); i += pos - i - 1; component = old; continue; } - builder.append(c); + builder.append( c ); } - if (builder.length() > 0) { - component.setText(builder.toString()); - components.add(component); + if (builder.length() > 0) + { + component.setText( builder.toString() ); + components.add( component ); } - return components.toArray(new BaseComponent[components.size()]); + return components.toArray( new BaseComponent[components.size()] ); } private String text; - public TextComponent(TextComponent old) { - super(old); - setText(old.getText()); + public TextComponent(TextComponent old) + { + super( old ); + setText( old.getText() ); } @Override - protected void toPlainText(StringBuilder builder) { - builder.append(text); - super.toPlainText(builder); + protected void toPlainText(StringBuilder builder) + { + builder.append( text ); + super.toPlainText( builder ); } @Override - protected void toLegacyText(StringBuilder builder) { - builder.append(getColor()); - if (isBold()) builder.append(ChatColor.BOLD); - if (isItalic()) builder.append(ChatColor.ITALIC); - if (isUnderlined()) builder.append(ChatColor.UNDERLINE); - if (isStrikethrough()) builder.append(ChatColor.STRIKETHROUGH); - if (isObfuscated()) builder.append(ChatColor.MAGIC); - builder.append(text); - super.toLegacyText(builder); + protected void toLegacyText(StringBuilder builder) + { + builder.append( getColor() ); + if (isBold()) builder.append( ChatColor.BOLD ); + if (isItalic()) builder.append( ChatColor.ITALIC ); + if (isUnderlined()) builder.append( ChatColor.UNDERLINE ); + if (isStrikethrough()) builder.append( ChatColor.STRIKETHROUGH ); + if (isObfuscated()) builder.append( ChatColor.MAGIC ); + builder.append( text ); + super.toLegacyText( builder ); } @Override - public String toString() { - return String.format("TextComponent{text=%s, %s}", text, super.toString()); + public String toString() + { + return String.format( "TextComponent{text=%s, %s}", text, super.toString() ); } } diff --git a/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java index bdad73aa..51695adb 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java @@ -10,44 +10,54 @@ import java.util.List; @Getter @Setter @NoArgsConstructor -public class TranslatableComponent extends BaseComponent { +public class TranslatableComponent extends BaseComponent +{ private String translate; private List with; - public TranslatableComponent(String translate, Object ...with) { - setTranslate(translate); + public TranslatableComponent(String translate, Object... with) + { + setTranslate( translate ); this.with = new ArrayList<>(); - for (Object w : with) { - if (w instanceof String) { - this.with.add(new TextComponent((String) w)); - } else { - this.with.add((BaseComponent) w); + for (Object w : with) + { + if (w instanceof String) + { + this.with.add( new TextComponent( (String) w ) ); + } else + { + this.with.add( (BaseComponent) w ); } } } - public void setWith(List components) { - for (BaseComponent component : components) { + public void setWith(List components) + { + for (BaseComponent component : components) + { component.parent = this; } with = components; } @Override - protected void toPlainText(StringBuilder builder) { + protected void toPlainText(StringBuilder builder) + { //TODO - super.toPlainText(builder); + super.toPlainText( builder ); } @Override - protected void toLegacyText(StringBuilder builder) { + protected void toLegacyText(StringBuilder builder) + { //TODO - super.toLegacyText(builder); + super.toLegacyText( builder ); } @Override - public String toString() { - return String.format("TranslatableComponent{translate=%s, with=%s, %s}", translate, with, super.toString()); + public String toString() + { + return String.format( "TranslatableComponent{translate=%s, with=%s, %s}", translate, with, super.toString() ); } } diff --git a/api/src/main/java/net/md_5/bungee/api/event/ServerKickEvent.java b/api/src/main/java/net/md_5/bungee/api/event/ServerKickEvent.java index 7df8d969..2208b414 100644 --- a/api/src/main/java/net/md_5/bungee/api/event/ServerKickEvent.java +++ b/api/src/main/java/net/md_5/bungee/api/event/ServerKickEvent.java @@ -61,11 +61,11 @@ public class ServerKickEvent extends Event implements Cancellable @Deprecated public String getKickReason() { - return BaseComponent.toLegacyText(kickReasonComponent); + return BaseComponent.toLegacyText( kickReasonComponent ); } @Deprecated public void setKickReason(String reason) { - kickReasonComponent = TextComponent.fromLegacyText(reason); + kickReasonComponent = TextComponent.fromLegacyText( reason ); } } 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 80ca6ee9..0f7da4bc 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -490,19 +490,21 @@ public class BungeeCord extends ProxyServer public void broadcast(String message) { getConsole().sendMessage( message ); - broadcast(TextComponent.fromLegacyText(message)); + broadcast( TextComponent.fromLegacyText( message ) ); } @Override - public void broadcast(BaseComponent[] message) { - getConsole().sendMessage(BaseComponent.toLegacyText(message)); - broadcast(new Chat(ComponentSerializer.toString(message))); + public void broadcast(BaseComponent[] message) + { + getConsole().sendMessage( BaseComponent.toLegacyText( message ) ); + broadcast( new Chat( ComponentSerializer.toString( message ) ) ); } @Override - public void broadcast(BaseComponent message) { + public void broadcast(BaseComponent message) + { getConsole().sendMessage( message.toLegacyText() ); - broadcast( new Chat(ComponentSerializer.toString(message)) ); + broadcast( new Chat( ComponentSerializer.toString( message ) ) ); } public void addConnection(UserConnection con) diff --git a/proxy/src/main/java/net/md_5/bungee/ServerConnection.java b/proxy/src/main/java/net/md_5/bungee/ServerConnection.java index f1490c64..75f6ca40 100644 --- a/proxy/src/main/java/net/md_5/bungee/ServerConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/ServerConnection.java @@ -43,15 +43,16 @@ public class ServerConnection implements Server @Override public synchronized void disconnect(String reason) { - disconnect( TextComponent.fromLegacyText(reason) ); + disconnect( TextComponent.fromLegacyText( reason ) ); } @Override - public void disconnect(BaseComponent[] reason) { - if ( !ch.isClosed() ) + public void disconnect(BaseComponent[] reason) + { + if (!ch.isClosed()) { // TODO: Can we just use a future here? - unsafe().sendPacket( new Kick(ComponentSerializer.toString(reason) ) ); + unsafe().sendPacket( new Kick( ComponentSerializer.toString( reason ) ) ); ch.getHandle().eventLoop().schedule( new Runnable() { @Override @@ -65,8 +66,9 @@ public class ServerConnection implements Server } @Override - public void disconnect(BaseComponent reason) { - disconnect(new BaseComponent[]{reason}); + public void disconnect(BaseComponent reason) + { + disconnect( new BaseComponent[]{reason} ); } @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 85d3adb1..f7f0e5fa 100644 --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java @@ -259,27 +259,29 @@ public final class UserConnection implements ProxiedPlayer @Override public synchronized void disconnect(String reason) { - disconnect0( TextComponent.fromLegacyText(reason) ); + disconnect0( TextComponent.fromLegacyText( reason ) ); } @Override - public void disconnect(BaseComponent[] reason) { + public void disconnect(BaseComponent[] reason) + { disconnect0( reason ); } @Override - public void disconnect(BaseComponent reason) { - disconnect0(new BaseComponent[]{reason}); + public void disconnect(BaseComponent reason) + { + disconnect0( new BaseComponent[]{reason} ); } public synchronized void disconnect0(BaseComponent[] reason) { - if ( ch.getHandle().isActive() ) + if (ch.getHandle().isActive()) { - bungee.getLogger().log( Level.INFO, "[" + getName() + "] disconnected with: " + BaseComponent.toLegacyText(reason) ); - unsafe().sendPacket( new Kick( ComponentSerializer.toString(reason) ) ); + bungee.getLogger().log( Level.INFO, "[" + getName() + "] disconnected with: " + BaseComponent.toLegacyText( reason ) ); + unsafe().sendPacket( new Kick( ComponentSerializer.toString( reason ) ) ); ch.close(); - if ( server != null ) + if (server != null) { server.disconnect( "Quitting" ); } @@ -296,25 +298,27 @@ public final class UserConnection implements ProxiedPlayer @Override public void sendMessage(String message) { - sendMessage(TextComponent.fromLegacyText(message)); + sendMessage( TextComponent.fromLegacyText( message ) ); } @Override public void sendMessages(String... messages) { - for ( String message : messages ) + for (String message : messages) { sendMessage( message ); } } @Override - public void sendMessage(BaseComponent[] message) { + public void sendMessage(BaseComponent[] message) + { unsafe().sendPacket( new Chat( ComponentSerializer.toString( message ) ) ); } @Override - public void sendMessage(BaseComponent message) { + public void sendMessage(BaseComponent message) + { unsafe().sendPacket( new Chat( ComponentSerializer.toString( message ) ) ); } diff --git a/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java b/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java index 60e9049a..ffcfc2fa 100644 --- a/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java +++ b/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java @@ -10,86 +10,109 @@ import net.md_5.bungee.api.chat.HoverEvent; import java.util.Arrays; -public class BaseComponentSerializer { +public class BaseComponentSerializer +{ - protected void deserialize(JsonObject object, BaseComponent component, JsonDeserializationContext context) { - if (object.has("color")) { - component.setColor(ChatColor.valueOf(object.get("color").getAsString().toUpperCase())); + protected void deserialize(JsonObject object, BaseComponent component, JsonDeserializationContext context) + { + if (object.has( "color" )) + { + component.setColor( ChatColor.valueOf( object.get( "color" ).getAsString().toUpperCase() ) ); } - if (object.has("bold")) { - component.setBold(object.get("bold").getAsBoolean()); + if (object.has( "bold" )) + { + component.setBold( object.get( "bold" ).getAsBoolean() ); } - if (object.has("italic")) { - component.setItalic(object.get("italic").getAsBoolean()); + if (object.has( "italic" )) + { + component.setItalic( object.get( "italic" ).getAsBoolean() ); } - if (object.has("underlined")) { - component.setUnderlined(object.get("underlined").getAsBoolean()); + if (object.has( "underlined" )) + { + component.setUnderlined( object.get( "underlined" ).getAsBoolean() ); } - if (object.has("strikethrough")) { - component.setUnderlined(object.get("strikethrough").getAsBoolean()); + if (object.has( "strikethrough" )) + { + component.setUnderlined( object.get( "strikethrough" ).getAsBoolean() ); } - if (object.has("obfuscated")) { - component.setUnderlined(object.get("obfuscated").getAsBoolean()); + if (object.has( "obfuscated" )) + { + component.setUnderlined( object.get( "obfuscated" ).getAsBoolean() ); } - if (object.has("extra")) { - component.setExtra(Arrays.asList((BaseComponent[])context.deserialize(object.get("extra"), BaseComponent[].class))); + if (object.has( "extra" )) + { + component.setExtra( Arrays.asList( (BaseComponent[]) context.deserialize( object.get( "extra" ), BaseComponent[].class ) ) ); } //Events - if (object.has("clickEvent")) { - JsonObject event = object.getAsJsonObject("clickEvent"); - component.setClickEvent(new ClickEvent( - ClickEvent.Action.valueOf(event.get("action").getAsString().toUpperCase()), - event.get("value").getAsString())); + if (object.has( "clickEvent" )) + { + JsonObject event = object.getAsJsonObject( "clickEvent" ); + component.setClickEvent( new ClickEvent( + ClickEvent.Action.valueOf( event.get( "action" ).getAsString().toUpperCase() ), + event.get( "value" ).getAsString() ) ); } - if (object.has("hoverEvent")) { - JsonObject event = object.getAsJsonObject("hoverEvent"); + if (object.has( "hoverEvent" )) + { + JsonObject event = object.getAsJsonObject( "hoverEvent" ); HoverEvent hoverEvent = new HoverEvent(); - hoverEvent.setAction(HoverEvent.Action.valueOf(event.get("action").getAsString().toUpperCase())); - Object res = context.deserialize(event.get("value"), BaseComponent.class); - if (res instanceof String) { - hoverEvent.setValue((String) res); - } else { - hoverEvent.setValue((BaseComponent) res); + hoverEvent.setAction( HoverEvent.Action.valueOf( event.get( "action" ).getAsString().toUpperCase() ) ); + Object res = context.deserialize( event.get( "value" ), BaseComponent.class ); + if (res instanceof String) + { + hoverEvent.setValue( (String) res ); + } else + { + hoverEvent.setValue( (BaseComponent) res ); } - component.setHoverEvent(hoverEvent); + component.setHoverEvent( hoverEvent ); } } - protected void serialize(JsonObject object, BaseComponent component, JsonSerializationContext context) { - if (component.getColorRaw() != null) { - object.addProperty("color", component.getColorRaw().getName()); + protected void serialize(JsonObject object, BaseComponent component, JsonSerializationContext context) + { + if (component.getColorRaw() != null) + { + object.addProperty( "color", component.getColorRaw().getName() ); } - if (component.isBoldRaw() != null) { - object.addProperty("bold", component.isBoldRaw()); + if (component.isBoldRaw() != null) + { + object.addProperty( "bold", component.isBoldRaw() ); } - if (component.isItalicRaw() != null) { - object.addProperty("italic", component.isItalicRaw()); + if (component.isItalicRaw() != null) + { + object.addProperty( "italic", component.isItalicRaw() ); } - if (component.isUnderlinedRaw() != null) { - object.addProperty("underlined", component.isUnderlinedRaw()); + if (component.isUnderlinedRaw() != null) + { + object.addProperty( "underlined", component.isUnderlinedRaw() ); } - if (component.isStrikethroughRaw() != null) { - object.addProperty("strikethrough", component.isStrikethroughRaw()); + if (component.isStrikethroughRaw() != null) + { + object.addProperty( "strikethrough", component.isStrikethroughRaw() ); } - if (component.isObfuscatedRaw() != null) { - object.addProperty("obfuscated", component.isObfuscatedRaw()); + if (component.isObfuscatedRaw() != null) + { + object.addProperty( "obfuscated", component.isObfuscatedRaw() ); } - if (component.getExtra() != null) { - object.add("extra", context.serialize(component.getExtra())); + if (component.getExtra() != null) + { + object.add( "extra", context.serialize( component.getExtra() ) ); } //Events - if (component.getClickEvent() != null) { + if (component.getClickEvent() != null) + { JsonObject clickEvent = new JsonObject(); - clickEvent.addProperty("action", component.getClickEvent().getAction().toString().toLowerCase()); - clickEvent.addProperty("value", component.getClickEvent().getValue()); + clickEvent.addProperty( "action", component.getClickEvent().getAction().toString().toLowerCase() ); + clickEvent.addProperty( "value", component.getClickEvent().getValue() ); } - if (component.getHoverEvent() != null) { + if (component.getHoverEvent() != null) + { JsonObject clickEvent = new JsonObject(); - clickEvent.addProperty("action", component.getHoverEvent().getAction().toString().toLowerCase()); - clickEvent.add("value", context.serialize(component.getHoverEvent().getValue())); + clickEvent.addProperty( "action", component.getHoverEvent().getAction().toString().toLowerCase() ); + clickEvent.add( "value", context.serialize( component.getHoverEvent().getValue() ) ); } } } diff --git a/proxy/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java b/proxy/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java index 3dc4fe79..e853d270 100644 --- a/proxy/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java +++ b/proxy/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java @@ -1,59 +1,66 @@ package net.md_5.bungee.chat; -import com.google.gson.*; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import net.md_5.bungee.api.ChatColor; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; import net.md_5.bungee.api.chat.BaseComponent; -import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.TranslatableComponent; -import javax.xml.soap.Text; -import java.io.IOException; import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -public class ComponentSerializer implements JsonSerializer, JsonDeserializer { +public class ComponentSerializer implements JsonSerializer, JsonDeserializer +{ private final static Gson gson = new GsonBuilder(). - registerTypeAdapter(BaseComponent.class, new ComponentSerializer()). - registerTypeAdapter(TextComponent.class, new TextComponentSerializer()). - registerTypeAdapter(TranslatableComponent.class, new TranslatableComponentSerializer()). + registerTypeAdapter( BaseComponent.class, new ComponentSerializer() ). + registerTypeAdapter( TextComponent.class, new TextComponentSerializer() ). + registerTypeAdapter( TranslatableComponent.class, new TranslatableComponentSerializer() ). create(); - public static BaseComponent[] parse(String json) { - if (json.startsWith("[")) { //Array - return gson.fromJson(json, BaseComponent[].class); + public static BaseComponent[] parse(String json) + { + if (json.startsWith( "[" )) + { //Array + return gson.fromJson( json, BaseComponent[].class ); } - return new BaseComponent[]{gson.fromJson(json, BaseComponent.class)}; + return new BaseComponent[]{gson.fromJson( json, BaseComponent.class )}; } - public static String toString(BaseComponent component) { - return gson.toJson(component); + public static String toString(BaseComponent component) + { + return gson.toJson( component ); } - public static String toString(BaseComponent[] components) { - return gson.toJson(components); + public static String toString(BaseComponent[] components) + { + return gson.toJson( components ); } @Override - public BaseComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - if (json.isJsonPrimitive()) { - return new TextComponent(json.getAsString()); + public BaseComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException + { + if (json.isJsonPrimitive()) + { + return new TextComponent( json.getAsString() ); } JsonObject object = json.getAsJsonObject(); - if (object.has("translate")) { - return context.deserialize(json, TranslatableComponent.class); + if (object.has( "translate" )) + { + return context.deserialize( json, TranslatableComponent.class ); } - return context.deserialize(json, TextComponent.class); + return context.deserialize( json, TextComponent.class ); } @Override - public JsonElement serialize(BaseComponent src, Type typeOfSrc, JsonSerializationContext context) { - return context.serialize(src, src.getClass()); + public JsonElement serialize(BaseComponent src, Type typeOfSrc, JsonSerializationContext context) + { + return context.serialize( src, src.getClass() ); } } diff --git a/proxy/src/main/java/net/md_5/bungee/chat/TextComponentSerializer.java b/proxy/src/main/java/net/md_5/bungee/chat/TextComponentSerializer.java index a8d758e3..246b47b8 100644 --- a/proxy/src/main/java/net/md_5/bungee/chat/TextComponentSerializer.java +++ b/proxy/src/main/java/net/md_5/bungee/chat/TextComponentSerializer.java @@ -1,28 +1,39 @@ package net.md_5.bungee.chat; -import com.google.gson.*; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; import net.md_5.bungee.api.chat.TextComponent; import java.lang.reflect.Type; -public class TextComponentSerializer extends BaseComponentSerializer implements JsonSerializer , JsonDeserializer{ +public class TextComponentSerializer extends BaseComponentSerializer implements JsonSerializer, JsonDeserializer +{ @Override - public TextComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + public TextComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException + { TextComponent component = new TextComponent(); JsonObject object = json.getAsJsonObject(); - deserialize(object, component, context); - component.setText(object.get("text").getAsString()); + deserialize( object, component, context ); + component.setText( object.get( "text" ).getAsString() ); return component; } @Override - public JsonElement serialize(TextComponent src, Type typeOfSrc, JsonSerializationContext context) { - if (!src.hasFormatting()) { - return new JsonPrimitive(src.getText()); + public JsonElement serialize(TextComponent src, Type typeOfSrc, JsonSerializationContext context) + { + if (!src.hasFormatting()) + { + return new JsonPrimitive( src.getText() ); } JsonObject object = new JsonObject(); - serialize(object, src, context); - object.addProperty("text", src.getText()); + serialize( object, src, context ); + object.addProperty( "text", src.getText() ); return object; } } diff --git a/proxy/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java b/proxy/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java index 364c286c..97e1a02c 100644 --- a/proxy/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java +++ b/proxy/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java @@ -1,34 +1,43 @@ package net.md_5.bungee.chat; -import com.google.gson.*; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; import net.md_5.bungee.api.chat.BaseComponent; -import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.TranslatableComponent; import java.lang.reflect.Type; -import java.util.ArrayList; import java.util.Arrays; -public class TranslatableComponentSerializer extends BaseComponentSerializer implements JsonSerializer , JsonDeserializer{ +public class TranslatableComponentSerializer extends BaseComponentSerializer implements JsonSerializer, JsonDeserializer +{ @Override - public TranslatableComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + public TranslatableComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException + { TranslatableComponent component = new TranslatableComponent(); JsonObject object = json.getAsJsonObject(); - deserialize(object, component, context); - component.setTranslate(object.get("translate").getAsString()); - if (object.has("with")) { - component.setWith(Arrays.asList((BaseComponent[])context.deserialize(object.get("with"), BaseComponent[].class))); + deserialize( object, component, context ); + component.setTranslate( object.get( "translate" ).getAsString() ); + if (object.has( "with" )) + { + component.setWith( Arrays.asList( (BaseComponent[]) context.deserialize( object.get( "with" ), BaseComponent[].class ) ) ); } return component; } @Override - public JsonElement serialize(TranslatableComponent src, Type typeOfSrc, JsonSerializationContext context) { + public JsonElement serialize(TranslatableComponent src, Type typeOfSrc, JsonSerializationContext context) + { JsonObject object = new JsonObject(); - serialize(object, src, context); - object.addProperty("translate", src.getTranslate()); - if (src.getWith() != null) { - object.add("with", context.serialize(src.getWith())); + serialize( object, src, context ); + object.addProperty( "translate", src.getTranslate() ); + if (src.getWith() != null) + { + object.add( "with", context.serialize( src.getWith() ) ); } return object; } diff --git a/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java b/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java index 42a11eb3..3e29efbb 100644 --- a/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java +++ b/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java @@ -29,19 +29,21 @@ public class ConsoleCommandSender implements CommandSender @Override public void sendMessages(String... messages) { - for ( String message : messages ) + for (String message : messages) { sendMessage( message ); } } @Override - public void sendMessage(BaseComponent[] message) { - sendMessage(BaseComponent.toLegacyText(message)); + public void sendMessage(BaseComponent[] message) + { + sendMessage( BaseComponent.toLegacyText( message ) ); } @Override - public void sendMessage(BaseComponent message) { + public void sendMessage(BaseComponent message) + { sendMessage( message.toLegacyText() ); } diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java index 52179ea5..04ba907d 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java @@ -398,25 +398,27 @@ public class InitialHandler extends PacketHandler implements PendingConnection @Override public synchronized void disconnect(String reason) { - if ( !ch.isClosed() ) + if (!ch.isClosed()) { - unsafe().sendPacket( new Kick( ComponentSerializer.toString(TextComponent.fromLegacyText(reason)) ) ); + unsafe().sendPacket( new Kick( ComponentSerializer.toString( TextComponent.fromLegacyText( reason ) ) ) ); ch.close(); } } @Override - public void disconnect(BaseComponent[] reason) { - if ( !ch.isClosed() ) + public void disconnect(BaseComponent[] reason) + { + if (!ch.isClosed()) { - unsafe().sendPacket( new Kick( ComponentSerializer.toString(reason) ) ); + unsafe().sendPacket( new Kick( ComponentSerializer.toString( reason ) ) ); ch.close(); } } @Override - public void disconnect(BaseComponent reason) { - disconnect(new BaseComponent[]{reason}); + public void disconnect(BaseComponent reason) + { + disconnect( new BaseComponent[]{reason} ); } @Override From 890fac27c5f64cabefbc186e7174404c8e0eccbe Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Fri, 6 Dec 2013 21:53:11 +0000 Subject: [PATCH 04/18] More formatting fixes --- .../md_5/bungee/api/chat/BaseComponent.java | 30 +++++++-------- .../md_5/bungee/api/chat/TextComponent.java | 30 +++++++-------- .../api/chat/TranslatableComponent.java | 6 +-- .../net/md_5/bungee/ServerConnection.java | 2 +- .../java/net/md_5/bungee/UserConnection.java | 6 +-- .../bungee/chat/BaseComponentSerializer.java | 38 +++++++++---------- .../md_5/bungee/chat/ComponentSerializer.java | 6 +-- .../bungee/chat/TextComponentSerializer.java | 2 +- .../chat/TranslatableComponentSerializer.java | 4 +- .../bungee/command/ConsoleCommandSender.java | 2 +- .../bungee/connection/InitialHandler.java | 2 +- 11 files changed, 64 insertions(+), 64 deletions(-) diff --git a/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java index 53eae97d..4162267d 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java @@ -53,7 +53,7 @@ public abstract class BaseComponent public static String toLegacyText(BaseComponent[] components) { StringBuilder builder = new StringBuilder(); - for (BaseComponent msg : components) + for ( BaseComponent msg : components ) { builder.append( msg.toLegacyText() ); } @@ -63,7 +63,7 @@ public abstract class BaseComponent public static String toPlainText(BaseComponent[] components) { StringBuilder builder = new StringBuilder(); - for (BaseComponent msg : components) + for ( BaseComponent msg : components ) { builder.append( msg.toPlainText() ); } @@ -80,9 +80,9 @@ public abstract class BaseComponent */ public ChatColor getColor() { - if (color == null) + if ( color == null ) { - if (parent == null) + if ( parent == null ) { return ChatColor.WHITE; } @@ -111,7 +111,7 @@ public abstract class BaseComponent */ public boolean isBold() { - if (bold == null) + if ( bold == null ) { return parent != null && parent.isBold(); } @@ -138,7 +138,7 @@ public abstract class BaseComponent */ public boolean isItalic() { - if (italic == null) + if ( italic == null ) { return parent != null && parent.isItalic(); } @@ -165,7 +165,7 @@ public abstract class BaseComponent */ public boolean isUnderlined() { - if (underlined == null) + if ( underlined == null ) { return parent != null && parent.isUnderlined(); } @@ -192,7 +192,7 @@ public abstract class BaseComponent */ public boolean isStrikethrough() { - if (strikethrough == null) + if ( strikethrough == null ) { return parent != null && parent.isStrikethrough(); } @@ -219,7 +219,7 @@ public abstract class BaseComponent */ public boolean isObfuscated() { - if (obfuscated == null) + if ( obfuscated == null ) { return parent != null && parent.isObfuscated(); } @@ -239,7 +239,7 @@ public abstract class BaseComponent public void setExtra(List components) { - for (BaseComponent component : components) + for ( BaseComponent component : components ) { component.parent = this; } @@ -265,7 +265,7 @@ public abstract class BaseComponent */ public void addExtra(BaseComponent component) { - if (extra == null) + if ( extra == null ) { extra = new ArrayList<>(); } @@ -289,9 +289,9 @@ public abstract class BaseComponent protected void toPlainText(StringBuilder builder) { - if (extra != null) + if ( extra != null ) { - for (BaseComponent e : extra) + for ( BaseComponent e : extra ) { e.toPlainText( builder ); } @@ -307,9 +307,9 @@ public abstract class BaseComponent protected void toLegacyText(StringBuilder builder) { - if (extra != null) + if ( extra != null ) { - for (BaseComponent e : extra) + for ( BaseComponent e : extra ) { e.toLegacyText( builder ); } diff --git a/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java index 723183f2..d4e79706 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java @@ -26,18 +26,18 @@ public class TextComponent extends BaseComponent TextComponent component = new TextComponent(); Matcher matcher = url.matcher( message ); - for (int i = 0; i < message.length(); i++) + for ( int i = 0; i < message.length(); i++ ) { char c = message.charAt( i ); - if (c == ChatColor.COLOR_CHAR) + if ( c == ChatColor.COLOR_CHAR ) { i++; c = message.charAt( i ); - if (c >= 'A' && c <= 'Z') + if ( c >= 'A' && c <= 'Z' ) { c += 32; } - if (builder.length() > 0) + if ( builder.length() > 0 ) { TextComponent old = component; component = new TextComponent( old ); @@ -46,7 +46,7 @@ public class TextComponent extends BaseComponent components.add( old ); } ChatColor format = ChatColor.getByChar( c ); - switch (format) + switch ( format ) { case BOLD: component.setBold( true ); @@ -73,11 +73,11 @@ public class TextComponent extends BaseComponent continue; } int pos = message.indexOf( ' ', i ); - if (pos == -1) pos = message.length(); - if (matcher.region( i, pos ).find()) + if ( pos == -1 ) pos = message.length(); + if ( matcher.region( i, pos ).find() ) { //Web link handling - if (builder.length() > 0) + if ( builder.length() > 0 ) { TextComponent old = component; component = new TextComponent( old ); @@ -91,7 +91,7 @@ public class TextComponent extends BaseComponent ClickEvent clickEvent = new ClickEvent(); clickEvent.setAction( ClickEvent.Action.OPEN_URL ); String urlString = message.substring( i, pos ); - if (urlString.startsWith( "http" )) + if ( urlString.startsWith( "http" ) ) { component.setText( urlString ); clickEvent.setValue( urlString ); @@ -108,7 +108,7 @@ public class TextComponent extends BaseComponent } builder.append( c ); } - if (builder.length() > 0) + if ( builder.length() > 0 ) { component.setText( builder.toString() ); components.add( component ); @@ -135,11 +135,11 @@ public class TextComponent extends BaseComponent protected void toLegacyText(StringBuilder builder) { builder.append( getColor() ); - if (isBold()) builder.append( ChatColor.BOLD ); - if (isItalic()) builder.append( ChatColor.ITALIC ); - if (isUnderlined()) builder.append( ChatColor.UNDERLINE ); - if (isStrikethrough()) builder.append( ChatColor.STRIKETHROUGH ); - if (isObfuscated()) builder.append( ChatColor.MAGIC ); + if ( isBold() ) builder.append( ChatColor.BOLD ); + if ( isItalic() ) builder.append( ChatColor.ITALIC ); + if ( isUnderlined() ) builder.append( ChatColor.UNDERLINE ); + if ( isStrikethrough() ) builder.append( ChatColor.STRIKETHROUGH ); + if ( isObfuscated() ) builder.append( ChatColor.MAGIC ); builder.append( text ); super.toLegacyText( builder ); } diff --git a/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java index 51695adb..5cfdce3b 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java @@ -20,9 +20,9 @@ public class TranslatableComponent extends BaseComponent { setTranslate( translate ); this.with = new ArrayList<>(); - for (Object w : with) + for ( Object w : with ) { - if (w instanceof String) + if ( w instanceof String ) { this.with.add( new TextComponent( (String) w ) ); } else @@ -34,7 +34,7 @@ public class TranslatableComponent extends BaseComponent public void setWith(List components) { - for (BaseComponent component : components) + for ( BaseComponent component : components ) { component.parent = this; } diff --git a/proxy/src/main/java/net/md_5/bungee/ServerConnection.java b/proxy/src/main/java/net/md_5/bungee/ServerConnection.java index 75f6ca40..e86240bd 100644 --- a/proxy/src/main/java/net/md_5/bungee/ServerConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/ServerConnection.java @@ -49,7 +49,7 @@ public class ServerConnection implements Server @Override public void disconnect(BaseComponent[] reason) { - if (!ch.isClosed()) + if ( !ch.isClosed() ) { // TODO: Can we just use a future here? unsafe().sendPacket( new Kick( ComponentSerializer.toString( reason ) ) ); 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 f7f0e5fa..6045b21f 100644 --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java @@ -276,12 +276,12 @@ public final class UserConnection implements ProxiedPlayer public synchronized void disconnect0(BaseComponent[] reason) { - if (ch.getHandle().isActive()) + if ( ch.getHandle().isActive() ) { bungee.getLogger().log( Level.INFO, "[" + getName() + "] disconnected with: " + BaseComponent.toLegacyText( reason ) ); unsafe().sendPacket( new Kick( ComponentSerializer.toString( reason ) ) ); ch.close(); - if (server != null) + if ( server != null ) { server.disconnect( "Quitting" ); } @@ -304,7 +304,7 @@ public final class UserConnection implements ProxiedPlayer @Override public void sendMessages(String... messages) { - for (String message : messages) + for ( String message : messages ) { sendMessage( message ); } diff --git a/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java b/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java index ffcfc2fa..65c3e6a4 100644 --- a/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java +++ b/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java @@ -15,50 +15,50 @@ public class BaseComponentSerializer protected void deserialize(JsonObject object, BaseComponent component, JsonDeserializationContext context) { - if (object.has( "color" )) + if ( object.has( "color" ) ) { component.setColor( ChatColor.valueOf( object.get( "color" ).getAsString().toUpperCase() ) ); } - if (object.has( "bold" )) + if ( object.has( "bold" ) ) { component.setBold( object.get( "bold" ).getAsBoolean() ); } - if (object.has( "italic" )) + if ( object.has( "italic" ) ) { component.setItalic( object.get( "italic" ).getAsBoolean() ); } - if (object.has( "underlined" )) + if ( object.has( "underlined" ) ) { component.setUnderlined( object.get( "underlined" ).getAsBoolean() ); } - if (object.has( "strikethrough" )) + if ( object.has( "strikethrough" ) ) { component.setUnderlined( object.get( "strikethrough" ).getAsBoolean() ); } - if (object.has( "obfuscated" )) + if ( object.has( "obfuscated" ) ) { component.setUnderlined( object.get( "obfuscated" ).getAsBoolean() ); } - if (object.has( "extra" )) + if ( object.has( "extra" ) ) { component.setExtra( Arrays.asList( (BaseComponent[]) context.deserialize( object.get( "extra" ), BaseComponent[].class ) ) ); } //Events - if (object.has( "clickEvent" )) + if ( object.has( "clickEvent" ) ) { JsonObject event = object.getAsJsonObject( "clickEvent" ); component.setClickEvent( new ClickEvent( ClickEvent.Action.valueOf( event.get( "action" ).getAsString().toUpperCase() ), event.get( "value" ).getAsString() ) ); } - if (object.has( "hoverEvent" )) + if ( object.has( "hoverEvent" ) ) { JsonObject event = object.getAsJsonObject( "hoverEvent" ); HoverEvent hoverEvent = new HoverEvent(); hoverEvent.setAction( HoverEvent.Action.valueOf( event.get( "action" ).getAsString().toUpperCase() ) ); Object res = context.deserialize( event.get( "value" ), BaseComponent.class ); - if (res instanceof String) + if ( res instanceof String ) { hoverEvent.setValue( (String) res ); } else @@ -71,44 +71,44 @@ public class BaseComponentSerializer protected void serialize(JsonObject object, BaseComponent component, JsonSerializationContext context) { - if (component.getColorRaw() != null) + if ( component.getColorRaw() != null ) { object.addProperty( "color", component.getColorRaw().getName() ); } - if (component.isBoldRaw() != null) + if ( component.isBoldRaw() != null ) { object.addProperty( "bold", component.isBoldRaw() ); } - if (component.isItalicRaw() != null) + if ( component.isItalicRaw() != null ) { object.addProperty( "italic", component.isItalicRaw() ); } - if (component.isUnderlinedRaw() != null) + if ( component.isUnderlinedRaw() != null ) { object.addProperty( "underlined", component.isUnderlinedRaw() ); } - if (component.isStrikethroughRaw() != null) + if ( component.isStrikethroughRaw() != null ) { object.addProperty( "strikethrough", component.isStrikethroughRaw() ); } - if (component.isObfuscatedRaw() != null) + if ( component.isObfuscatedRaw() != null ) { object.addProperty( "obfuscated", component.isObfuscatedRaw() ); } - if (component.getExtra() != null) + if ( component.getExtra() != null ) { object.add( "extra", context.serialize( component.getExtra() ) ); } //Events - if (component.getClickEvent() != null) + if ( component.getClickEvent() != null ) { JsonObject clickEvent = new JsonObject(); clickEvent.addProperty( "action", component.getClickEvent().getAction().toString().toLowerCase() ); clickEvent.addProperty( "value", component.getClickEvent().getValue() ); } - if (component.getHoverEvent() != null) + if ( component.getHoverEvent() != null ) { JsonObject clickEvent = new JsonObject(); clickEvent.addProperty( "action", component.getHoverEvent().getAction().toString().toLowerCase() ); diff --git a/proxy/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java b/proxy/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java index e853d270..58dbc3f1 100644 --- a/proxy/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java +++ b/proxy/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java @@ -26,7 +26,7 @@ public class ComponentSerializer implements JsonSerializer, JsonD public static BaseComponent[] parse(String json) { - if (json.startsWith( "[" )) + if ( json.startsWith( "[" ) ) { //Array return gson.fromJson( json, BaseComponent[].class ); } @@ -46,12 +46,12 @@ public class ComponentSerializer implements JsonSerializer, JsonD @Override public BaseComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - if (json.isJsonPrimitive()) + if ( json.isJsonPrimitive() ) { return new TextComponent( json.getAsString() ); } JsonObject object = json.getAsJsonObject(); - if (object.has( "translate" )) + if ( object.has( "translate" ) ) { return context.deserialize( json, TranslatableComponent.class ); } diff --git a/proxy/src/main/java/net/md_5/bungee/chat/TextComponentSerializer.java b/proxy/src/main/java/net/md_5/bungee/chat/TextComponentSerializer.java index 246b47b8..031590d6 100644 --- a/proxy/src/main/java/net/md_5/bungee/chat/TextComponentSerializer.java +++ b/proxy/src/main/java/net/md_5/bungee/chat/TextComponentSerializer.java @@ -27,7 +27,7 @@ public class TextComponentSerializer extends BaseComponentSerializer implements @Override public JsonElement serialize(TextComponent src, Type typeOfSrc, JsonSerializationContext context) { - if (!src.hasFormatting()) + if ( !src.hasFormatting() ) { return new JsonPrimitive( src.getText() ); } diff --git a/proxy/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java b/proxy/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java index 97e1a02c..f48954a1 100644 --- a/proxy/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java +++ b/proxy/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java @@ -22,7 +22,7 @@ public class TranslatableComponentSerializer extends BaseComponentSerializer imp JsonObject object = json.getAsJsonObject(); deserialize( object, component, context ); component.setTranslate( object.get( "translate" ).getAsString() ); - if (object.has( "with" )) + if ( object.has( "with" ) ) { component.setWith( Arrays.asList( (BaseComponent[]) context.deserialize( object.get( "with" ), BaseComponent[].class ) ) ); } @@ -35,7 +35,7 @@ public class TranslatableComponentSerializer extends BaseComponentSerializer imp JsonObject object = new JsonObject(); serialize( object, src, context ); object.addProperty( "translate", src.getTranslate() ); - if (src.getWith() != null) + if ( src.getWith() != null ) { object.add( "with", context.serialize( src.getWith() ) ); } diff --git a/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java b/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java index 3e29efbb..ba159474 100644 --- a/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java +++ b/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java @@ -29,7 +29,7 @@ public class ConsoleCommandSender implements CommandSender @Override public void sendMessages(String... messages) { - for (String message : messages) + for ( String message : messages ) { sendMessage( message ); } diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java index 04ba907d..4a326c24 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java @@ -408,7 +408,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection @Override public void disconnect(BaseComponent[] reason) { - if (!ch.isClosed()) + if ( !ch.isClosed() ) { unsafe().sendPacket( new Kick( ComponentSerializer.toString( reason ) ) ); ch.close(); From 854b6faf0ec4933548fd3a518bf2df4b0ae6080d Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Fri, 6 Dec 2013 22:04:17 +0000 Subject: [PATCH 05/18] Fixed broadcasts getting double logged --- proxy/src/main/java/net/md_5/bungee/BungeeCord.java | 1 - 1 file changed, 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 0f7da4bc..baf0ee11 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -489,7 +489,6 @@ public class BungeeCord extends ProxyServer @Override public void broadcast(String message) { - getConsole().sendMessage( message ); broadcast( TextComponent.fromLegacyText( message ) ); } From e3e551d82586fa6702aef25cda7a23cd0f5f77ae Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Fri, 6 Dec 2013 23:00:32 +0000 Subject: [PATCH 06/18] Use varargs instead of arrays --- api/src/main/java/net/md_5/bungee/api/CommandSender.java | 2 +- api/src/main/java/net/md_5/bungee/api/ProxyServer.java | 2 +- .../main/java/net/md_5/bungee/api/chat/BaseComponent.java | 4 ++-- .../java/net/md_5/bungee/api/connection/Connection.java | 2 +- proxy/src/main/java/net/md_5/bungee/BungeeCord.java | 2 +- proxy/src/main/java/net/md_5/bungee/ServerConnection.java | 2 +- proxy/src/main/java/net/md_5/bungee/UserConnection.java | 8 ++++---- .../java/net/md_5/bungee/chat/ComponentSerializer.java | 2 +- .../net/md_5/bungee/command/ConsoleCommandSender.java | 2 +- .../java/net/md_5/bungee/connection/InitialHandler.java | 2 +- 10 files changed, 14 insertions(+), 14 deletions(-) diff --git a/api/src/main/java/net/md_5/bungee/api/CommandSender.java b/api/src/main/java/net/md_5/bungee/api/CommandSender.java index 96248935..4a8f6bcd 100644 --- a/api/src/main/java/net/md_5/bungee/api/CommandSender.java +++ b/api/src/main/java/net/md_5/bungee/api/CommandSender.java @@ -36,7 +36,7 @@ public interface CommandSender * * @param message the message to send */ - public void sendMessage(BaseComponent[] message); + public void sendMessage(BaseComponent... message); /** * Send a message to this sender. diff --git a/api/src/main/java/net/md_5/bungee/api/ProxyServer.java b/api/src/main/java/net/md_5/bungee/api/ProxyServer.java index 06155010..f2ef12d0 100644 --- a/api/src/main/java/net/md_5/bungee/api/ProxyServer.java +++ b/api/src/main/java/net/md_5/bungee/api/ProxyServer.java @@ -240,7 +240,7 @@ public abstract class ProxyServer * * @param message the message to broadcast */ - public abstract void broadcast(BaseComponent[] message); + public abstract void broadcast(BaseComponent... message); /** * Send the specified message to the console and all connected players. diff --git a/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java index 4162267d..f004a330 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java @@ -50,7 +50,7 @@ public abstract class BaseComponent setObfuscated( old.isObfuscatedRaw() ); } - public static String toLegacyText(BaseComponent[] components) + public static String toLegacyText(BaseComponent... components) { StringBuilder builder = new StringBuilder(); for ( BaseComponent msg : components ) @@ -60,7 +60,7 @@ public abstract class BaseComponent return builder.toString(); } - public static String toPlainText(BaseComponent[] components) + public static String toPlainText(BaseComponent... components) { StringBuilder builder = new StringBuilder(); for ( BaseComponent msg : components ) diff --git a/api/src/main/java/net/md_5/bungee/api/connection/Connection.java b/api/src/main/java/net/md_5/bungee/api/connection/Connection.java index fb146164..4e2068b9 100644 --- a/api/src/main/java/net/md_5/bungee/api/connection/Connection.java +++ b/api/src/main/java/net/md_5/bungee/api/connection/Connection.java @@ -39,7 +39,7 @@ public interface Connection * @param reason the reason shown to the player / sent to the server on * disconnect */ - void disconnect(BaseComponent[] reason); + void disconnect(BaseComponent... reason); /** * Disconnects this end of the connection for the specified reason. If this 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 baf0ee11..4b746c95 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -493,7 +493,7 @@ public class BungeeCord extends ProxyServer } @Override - public void broadcast(BaseComponent[] message) + public void broadcast(BaseComponent... message) { getConsole().sendMessage( BaseComponent.toLegacyText( message ) ); broadcast( new Chat( ComponentSerializer.toString( message ) ) ); diff --git a/proxy/src/main/java/net/md_5/bungee/ServerConnection.java b/proxy/src/main/java/net/md_5/bungee/ServerConnection.java index e86240bd..b8e35b12 100644 --- a/proxy/src/main/java/net/md_5/bungee/ServerConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/ServerConnection.java @@ -47,7 +47,7 @@ public class ServerConnection implements Server } @Override - public void disconnect(BaseComponent[] reason) + public void disconnect(BaseComponent... reason) { if ( !ch.isClosed() ) { 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 6045b21f..e51ec343 100644 --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java @@ -263,7 +263,7 @@ public final class UserConnection implements ProxiedPlayer } @Override - public void disconnect(BaseComponent[] reason) + public void disconnect(BaseComponent... reason) { disconnect0( reason ); } @@ -271,10 +271,10 @@ public final class UserConnection implements ProxiedPlayer @Override public void disconnect(BaseComponent reason) { - disconnect0( new BaseComponent[]{reason} ); + disconnect0( reason ); } - public synchronized void disconnect0(BaseComponent[] reason) + public synchronized void disconnect0(BaseComponent ...reason) { if ( ch.getHandle().isActive() ) { @@ -311,7 +311,7 @@ public final class UserConnection implements ProxiedPlayer } @Override - public void sendMessage(BaseComponent[] message) + public void sendMessage(BaseComponent... message) { unsafe().sendPacket( new Chat( ComponentSerializer.toString( message ) ) ); } diff --git a/proxy/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java b/proxy/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java index 58dbc3f1..c019f7ac 100644 --- a/proxy/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java +++ b/proxy/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java @@ -38,7 +38,7 @@ public class ComponentSerializer implements JsonSerializer, JsonD return gson.toJson( component ); } - public static String toString(BaseComponent[] components) + public static String toString(BaseComponent... components) { return gson.toJson( components ); } diff --git a/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java b/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java index ba159474..d6e8b79f 100644 --- a/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java +++ b/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java @@ -36,7 +36,7 @@ public class ConsoleCommandSender implements CommandSender } @Override - public void sendMessage(BaseComponent[] message) + public void sendMessage(BaseComponent... message) { sendMessage( BaseComponent.toLegacyText( message ) ); } diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java index 4a326c24..9d33c05e 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java @@ -406,7 +406,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection } @Override - public void disconnect(BaseComponent[] reason) + public void disconnect(BaseComponent... reason) { if ( !ch.isClosed() ) { From 696679809d645243f52e68047f01c0c3acb5745e Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Fri, 6 Dec 2013 23:18:10 +0000 Subject: [PATCH 07/18] Support printing TranslatableComponents --- .../api/chat/TranslatableComponent.java | 37 +- proxy/src/main/resources/en_US.properties | 1685 +++++++++++++++++ 2 files changed, 1720 insertions(+), 2 deletions(-) create mode 100644 proxy/src/main/resources/en_US.properties diff --git a/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java index 5cfdce3b..7081eb2b 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java @@ -3,15 +3,18 @@ package net.md_5.bungee.api.chat; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import net.md_5.bungee.api.ChatColor; import java.util.ArrayList; import java.util.List; +import java.util.ResourceBundle; @Getter @Setter @NoArgsConstructor public class TranslatableComponent extends BaseComponent { + public final ResourceBundle locales = ResourceBundle.getBundle( "en_US" ); private String translate; private List with; @@ -44,14 +47,44 @@ public class TranslatableComponent extends BaseComponent @Override protected void toPlainText(StringBuilder builder) { - //TODO + String[] parts = translate.split( "((?<=%s)|(?=%s))" ); + int i = 0; + for ( String part : parts ) + { + if ( part.equals( "%s" ) ) + { + with.get( i ).toPlainText( builder ); + i++; + } else + { + builder.append( part ); + } + } super.toPlainText( builder ); } @Override protected void toLegacyText(StringBuilder builder) { - //TODO + String[] parts = locales.getString( translate ).split( "((?<=%s)|(?=%s))" ); + int i = 0; + for ( String part : parts ) + { + if ( part.equals( "%s" ) ) + { + with.get( i ).toLegacyText( builder ); + i++; + } else + { + builder.append( getColor() ); + if ( isBold() ) builder.append( ChatColor.BOLD ); + if ( isItalic() ) builder.append( ChatColor.ITALIC ); + if ( isUnderlined() ) builder.append( ChatColor.UNDERLINE ); + if ( isStrikethrough() ) builder.append( ChatColor.STRIKETHROUGH ); + if ( isObfuscated() ) builder.append( ChatColor.MAGIC ); + builder.append( part ); + } + } super.toLegacyText( builder ); } diff --git a/proxy/src/main/resources/en_US.properties b/proxy/src/main/resources/en_US.properties new file mode 100644 index 00000000..4feb71f7 --- /dev/null +++ b/proxy/src/main/resources/en_US.properties @@ -0,0 +1,1685 @@ + +language.name=English +language.region=US +language.code=en_US + +gui.done=Done +gui.cancel=Cancel +gui.back=Back +gui.toMenu=Back to title screen +gui.up=Up +gui.down=Down +gui.yes=Yes +gui.no=No + +translation.test.none=Hello, world! +translation.test.complex=Prefix, %s%2$s again %s and %1$s lastly %s and also %1$s again! +translation.test.escape=%%s %%%s %%%%s %%%%%s +translation.test.invalid=hi % +translation.test.invalid2=hi % s +translation.test.args=%s %s +translation.test.world=world + +menu.singleplayer=Singleplayer +menu.multiplayer=Multiplayer +menu.online=Minecraft Realms +menu.options=Options... +menu.quit=Quit Game +menu.returnToMenu=Save and Quit to Title +menu.disconnect=Disconnect +menu.returnToGame=Back to Game +menu.switchingLevel=Switching worlds +menu.generatingLevel=Generating world +menu.loadingLevel=Loading world +menu.generatingTerrain=Building terrain +menu.convertingLevel=Converting world +menu.simulating=Simulating the world for a bit +menu.respawning=Respawning +menu.shareToLan=Open to LAN + +selectWorld.title=Select World +selectWorld.empty=empty +selectWorld.world=World +selectWorld.select=Play Selected World +selectWorld.create=Create New World +selectWorld.recreate=Re-Create +selectWorld.createDemo=Play New Demo World +selectWorld.delete=Delete +selectWorld.rename=Rename +selectWorld.deleteQuestion=Are you sure you want to delete this world? +selectWorld.deleteWarning=will be lost forever! (A long time!) +selectWorld.deleteButton=Delete +selectWorld.renameButton=Rename +selectWorld.renameTitle=Rename World +selectWorld.conversion=Must be converted! +selectWorld.newWorld=New World +selectWorld.newWorld.copyOf=Copy of %s +selectWorld.enterName=World Name +selectWorld.resultFolder=Will be saved in: +selectWorld.enterSeed=Seed for the World Generator +selectWorld.seedInfo=Leave blank for a random seed +selectWorld.cheats=Cheats +selectWorld.customizeType=Customize + +createWorld.customize.presets=Presets +createWorld.customize.presets.title=Select a Preset +createWorld.customize.presets.select=Use Preset +createWorld.customize.presets.share=Want to share your preset with someone? Use the below box! +createWorld.customize.presets.list=Alternatively, here's some we made earlier! +createWorld.customize.flat.title=Superflat Customization +createWorld.customize.flat.tile=Layer Material +createWorld.customize.flat.height=Height +createWorld.customize.flat.addLayer=Add Layer +createWorld.customize.flat.editLayer=Edit Layer +createWorld.customize.flat.removeLayer=Remove Layer +createWorld.customize.flat.layer.top=Top - %d +createWorld.customize.flat.layer=%d +createWorld.customize.flat.layer.bottom=Bottom - %d + +gameMode.survival=Survival Mode +gameMode.creative=Creative Mode +gameMode.adventure=Adventure Mode +gameMode.hardcore=Hardcore Mode! +gameMode.changed=Your game mode has been updated + +selectWorld.gameMode=Game Mode +selectWorld.gameMode.survival=Survival +selectWorld.gameMode.survival.line1=Search for resources, crafting, gain +selectWorld.gameMode.survival.line2=levels, health and hunger +selectWorld.gameMode.creative=Creative +selectWorld.gameMode.creative.line1=Unlimited resources, free flying and +selectWorld.gameMode.creative.line2=destroy blocks instantly +selectWorld.gameMode.hardcore=Hardcore +selectWorld.gameMode.hardcore.line1=Same as survival mode, locked at hardest +selectWorld.gameMode.hardcore.line2=difficulty, and one life only +selectWorld.gameMode.adventure=Adventure +selectWorld.gameMode.adventure.line1=Same as survival mode, but blocks can't +selectWorld.gameMode.adventure.line2=be added or removed +selectWorld.moreWorldOptions=More World Options... +selectWorld.mapFeatures=Generate Structures: +selectWorld.mapFeatures.info=Villages, dungeons etc +selectWorld.mapType=World Type: +selectWorld.mapType.normal=Normal +selectWorld.allowCommands=Allow Cheats: +selectWorld.allowCommands.info=Commands like /gamemode, /xp +selectWorld.hardcoreMode=Hardcore: +selectWorld.hardcoreMode.info=World is deleted upon death +selectWorld.bonusItems=Bonus Chest: + +generator.default=Default +generator.flat=Superflat +generator.largeBiomes=Large Biomes +generator.amplified=AMPLIFIED + +generator.amplified.info=Notice: Just for fun, requires beefy computer + +selectServer.title=Select Server +selectServer.empty=empty +selectServer.select=Join Server +selectServer.direct=Direct Connect +selectServer.edit=Edit +selectServer.delete=Delete +selectServer.add=Add server +selectServer.defaultName=Minecraft Server +selectServer.deleteQuestion=Are you sure you want to remove this server? +selectServer.deleteWarning=will be lost forever! (A long time!) +selectServer.deleteButton=Delete +selectServer.refresh=Refresh +selectServer.hiddenAddress=(Hidden) +addServer.title=Edit Server Info +addServer.enterName=Server Name +addServer.enterIp=Server Address +addServer.add=Done +addServer.hideAddress=Hide Address +lanServer.title=LAN World +lanServer.scanning=Scanning for games on your local network +lanServer.start=Start LAN World +lanServer.otherPlayers=Settings for Other Players +mcoServer.title=Minecraft Online World + +multiplayer.title=Play Multiplayer +multiplayer.connect=Connect +multiplayer.info1=Minecraft Multiplayer is currently not finished, but there +multiplayer.info2=is some buggy early testing going on. +multiplayer.ipinfo=Enter the IP of a server to connect to it: +multiplayer.texturePrompt.line1=This server recommends the use of a custom resource pack. +multiplayer.texturePrompt.line2=Would you like to download and install it automagically? +multiplayer.downloadingTerrain=Downloading terrain +multiplayer.downloadingStats=Downloading statistics & achievements... +multiplayer.stopSleeping=Leave Bed +multiplayer.player.joined=%s joined the game +multiplayer.player.left=%s left the game + +chat.cannotSend=Cannot send chat message +chat.type.text=<%s> %s +chat.type.emote=* %s %s +chat.type.announcement=[%s] %s +chat.type.admin=[%s: %s] +chat.type.achievement=%s has just earned the achievement %s +chat.link.confirm=Are you sure you want to open the following website? +chat.link.warning=Never open links from people that you don't trust! +chat.copy=Copy to Clipboard +chat.link.confirmTrusted=Do you want to open this link or copy it to your clipboard? +chat.link.open=Open in browser + +$o=Play Demo World +menu.resetdemo=Reset Demo World + +demo.day.1=This demo will last five game days, do your best! +demo.day.2=Day Two +demo.day.3=Day Three +demo.day.4=Day Four +demo.day.5=This is your last day! +demo.day.warning=Your time is almost up! +demo.day.6=You have passed your fifth day, use F2 to save a screenshot of your creation +demo.reminder=The demo time has expired, buy the game to continue or start a new world! +demo.remainingTime=Remaining time: %s +demo.demoExpired=Demo time's up! +demo.help.movement=Use %1$s, %2$s, %3$s, %4$s and the mouse to move around +demo.help.movementShort=Move by pressing %1$s, %2$s, %3$s, %4$s +demo.help.movementMouse=Look around using the mouse +demo.help.jump=Jump by pressing %1$s +demo.help.inventory=Use %1$s to open your inventory +demo.help.title=Minecraft Demo Mode +demo.help.fullWrapped=This demo will last 5 ingame days (about 1 hour and 40 minutes of real time). Check the achievements for hints! Have fun! +demo.help.buy=Purchase Now! +demo.help.later=Continue Playing! + +connect.connecting=Connecting to the server... +connect.authorizing=Logging in... +connect.failed=Failed to connect to the server + +disconnect.genericReason=%s +disconnect.disconnected=Disconnected by Server +disconnect.lost=Connection Lost +disconnect.kicked=Was kicked from the game +disconnect.timeout=Timed out +disconnect.closed=Connection closed +disconnect.loginFailed=Failed to login +disconnect.loginFailedInfo=Failed to login: %s +disconnect.loginFailedInfo.serversUnavailable=The authentication are currently down for maintenance. +disconnect.loginFailedInfo.invalidSession=Invalid session (Try restarting your game) +disconnect.quitting=Quitting +disconnect.endOfStream=End of stream +disconnect.overflow=Buffer overflow +disconnect.spam=Kicked for spamming + +soundCategory.master=Master Volume +soundCategory.music=Music +soundCategory.record=Jukebox/Noteblocks +soundCategory.weather=Weather +soundCategory.hostile=Hostile Creatures +soundCategory.neutral=Friendly Creatures +soundCategory.player=Players +soundCategory.block=Blocks +soundCategory.ambient=Ambient/Environment + +options.off=OFF +options.on=ON +options.visible=Shown +options.hidden=Hidden +options.title=Options +options.controls=Controls... +options.video=Video Settings... +options.language=Language... +options.sounds=Music & Sounds... +options.sounds.title=Music & Sound Options +options.languageWarning=Language translations may not be 100%% accurate +options.videoTitle=Video Settings +options.music=Music +options.sound=Sound +options.invertMouse=Invert Mouse +options.fov=FOV +options.fov.min=Normal +options.fov.max=Quake Pro +options.saturation=Saturation +options.gamma=Brightness +options.gamma.min=Moody +options.gamma.max=Bright +options.sensitivity=Sensitivity +options.sensitivity.min=*yawn* +options.sensitivity.max=HYPERSPEED!!! +options.renderDistance=Render Distance +options.renderDistance.tiny=Tiny +options.renderDistance.short=Short +options.renderDistance.normal=Normal +options.renderDistance.far=Far +options.viewBobbing=View Bobbing +options.ao=Smooth Lighting +options.ao.off=OFF +options.ao.min=Minimum +options.ao.max=Maximum +options.anaglyph=3D Anaglyph +options.framerateLimit=Max Framerate +options.framerateLimit.max=Unlimited +options.difficulty=Difficulty +options.difficulty.peaceful=Peaceful +options.difficulty.easy=Easy +options.difficulty.normal=Normal +options.difficulty.hard=Hard +options.difficulty.hardcore=Hardcore +options.graphics=Graphics +options.graphics.fancy=Fancy +options.graphics.fast=Fast +options.guiScale=GUI Scale +options.guiScale.auto=Auto +options.guiScale.small=Small +options.guiScale.normal=Normal +options.guiScale.large=Large +options.advancedOpengl=Advanced OpenGL +options.fboEnable=Enable FBOs +options.postProcessEnable=Enable Post-Processing +options.aoDesc0=Enable faux ambient occlusion on blocks. +options.aoDesc1= +options.framerateLimitDesc0=Selects the maximum frame rate: +options.framerateLimitDesc1=35fps, 120fps, or 200+fps. +options.viewBobbingDesc0=Enables view-bob when moving. +options.viewBobbingDesc1= +options.renderCloudsDesc0=Enables the rendering of clouds. +options.renderCloudsDesc1= +options.graphicsDesc0='Fancy': Enables extra transparency. +options.graphicsDesc1='Fast': Suggested for lower-end hardware. +options.renderDistanceDesc0=Maximum render distance. Smaller values +options.renderDistanceDesc1=run better on lower-end hardware. +options.particlesDesc0=Selects the overall amount of particles. +options.particlesDesc1=On lower-end hardware, less is better. +options.advancedOpenglDesc0=Enables occlusion queries. On AMD and Intel +options.advancedOpenglDesc1=hardware, this may decrease performance. +options.fboEnableDesc0=Enables the use of Framebuffer Objects. +options.fboEnableDesc1=Necessary for certain Minecraft features. +options.postProcessEnableDesc0=Enables post-processing. Disabling will +options.postProcessEnableDesc1=result in reduction in Awesome Levels. +options.renderClouds=Clouds +options.qualityButton=Video Quality Settings... +options.qualityVideoTitle=Video Quality Settings +options.performanceButton=Video Performance Settings... +options.performanceVideoTitle=Video Performance Settings +options.advancedButton=Advanced Video Settings... +options.advancedVideoTitle=Advanced Video Settings +options.postButton=Post-Processing Settings... +options.postVideoTitle=Post-Processing Settings +options.farWarning1=A 64 bit Java installation is recommended +options.farWarning2=for 'Far' render distance (you have 32 bit) +options.particles=Particles +options.particles.all=All +options.particles.decreased=Decreased +options.particles.minimal=Minimal +options.multiplayer.title=Multiplayer Settings... +options.chat.title=Chat Settings... +options.chat.visibility=Chat +options.chat.visibility.full=Shown +options.chat.visibility.system=Commands Only +options.chat.visibility.hidden=Hidden +options.chat.color=Colors +options.chat.opacity=Opacity +options.chat.links=Web Links +options.chat.links.prompt=Prompt on Links +options.chat.scale=Scale +options.chat.width=Width +options.chat.height.focused=Focused Height +options.chat.height.unfocused=Unfocused Height +options.showCape=Show Cape +options.serverTextures=Server Textures +options.snooper=Allow Snooper +options.snooper.view=Snooper Settings... +options.snooper.title=Machine Specs Collection +options.snooper.desc=We want to collect information about your machine to help improve Minecraft by knowing what we can support and where the biggest problems are. All of this information is completely anonymous and viewable below. We promise we won't do anything bad with this data, but if you want to opt out then feel free to toggle it off! +options.resourcepack=Resource Packs... +options.fullscreen=Fullscreen +options.vsync=Use VSync +options.touchscreen=Touchscreen Mode +options.mipmapLevels=Mipmap Levels +options.anisotropicFiltering=Anisotropic Filtering +options.forceUnicodeFont=Force Unicode Font + +controls.title=Controls +controls.reset=Reset +controls.resetAll=Reset Keys + +key.sprint=Sprint +key.forward=Walk Forwards +key.left=Strafe Left +key.back=Walk Backwards +key.right=Strafe Right +key.jump=Jump +key.inventory=Inventory +key.drop=Drop Item +key.chat=Open Chat +key.sneak=Sneak +key.playerlist=List Players +key.attack=Attack/Destroy +key.use=Use Item/Place Block +key.pickItem=Pick Block +key.mouseButton=Button %1$s +key.command=Open Command +key.screenshot=Take Screenshot +key.togglePerspective=Toggle Perspective +key.smoothCamera=Toggle Cinematic Camera +key.hotbar.1=Hotbar Slot 1 +key.hotbar.2=Hotbar Slot 2 +key.hotbar.3=Hotbar Slot 3 +key.hotbar.4=Hotbar Slot 4 +key.hotbar.5=Hotbar Slot 5 +key.hotbar.6=Hotbar Slot 6 +key.hotbar.7=Hotbar Slot 7 +key.hotbar.8=Hotbar Slot 8 +key.hotbar.9=Hotbar Slot 9 + +key.categories.movement=Movement +key.categories.misc=Miscellaneous +key.categories.multiplayer=Multiplayer +key.categories.gameplay=Gameplay +key.categories.ui=Game Interface +key.categories.inventory=Inventory + +resourcePack.openFolder=Open resource pack folder +resourcePack.title=Select Resource Packs +resourcePack.available.title=Available Resource Packs +resourcePack.selected.title=Selected Resource Packs +resourcePack.folderInfo=(Place resource pack files here) + +book.pageIndicator=Page %1$s of %2$s +book.byAuthor=by %1$s +book.signButton=Sign +book.editTitle=Enter Book Title: +book.finalizeButton=Sign and Close +book.finalizeWarning=Note! When you sign the book, it will no longer be editable. + +tile.stone.name=Stone +tile.hayBlock.name=Hay Bale +tile.grass.name=Grass Block +tile.dirt.default.name=Dirt +tile.dirt.podzol.name=Podzol +tile.stonebrick.name=Cobblestone +tile.wood.name=Wooden Planks +tile.wood.oak.name=Oak Wood Planks +tile.wood.spruce.name=Spruce Wood Planks +tile.wood.birch.name=Birch Wood Planks +tile.wood.jungle.name=Jungle Wood Planks +tile.wood.acacia.name=Acacia Wood Planks +tile.wood.big_oak.name=Dark Oak Wood Planks +tile.sapling.oak.name=Oak Sapling +tile.sapling.spruce.name=Spruce Sapling +tile.sapling.birch.name=Birch Sapling +tile.sapling.jungle.name=Jungle Sapling +tile.sapling.acacia.name=Acacia Sapling +tile.sapling.roofed_oak.name=Dark Oak Sapling +tile.deadbush.name=Dead Bush +tile.bedrock.name=Bedrock +tile.water.name=Water +tile.lava.name=Lava +tile.sand.default.name=Sand +tile.sand.red.name=Red Sand +tile.sandStone.name=Sandstone +tile.sandStone.default.name=Sandstone +tile.sandStone.chiseled.name=Chiseled Sandstone +tile.sandStone.smooth.name=Smooth Sandstone +tile.gravel.name=Gravel +tile.oreGold.name=Gold Ore +tile.oreIron.name=Iron Ore +tile.oreCoal.name=Coal Ore +tile.log.name=Wood +tile.log.oak.name=Oak Wood +tile.log.spruce.name=Spruce Wood +tile.log.birch.name=Birch Wood +tile.log.jungle.name=Jungle Wood +tile.log.acacia.name=Acacia Wood +tile.log.big_oak.name=Dark Oak Wood +tile.leaves.name=Leaves +tile.leaves.oak.name=Oak Leaves +tile.leaves.spruce.name=Spruce Leaves +tile.leaves.birch.name=Birch Leaves +tile.leaves.jungle.name=Jungle Leaves +tile.leaves.acacia.name=Acacia Leaves +tile.leaves.big_oak.name=Dark Oak Leaves +tile.tallgrass.name=Grass +tile.tallgrass.shrub.name=Shrub +tile.tallgrass.grass.name=Grass +tile.tallgrass.fern.name=Fern +tile.sponge.name=Sponge +tile.glass.name=Glass +tile.stainedGlass.name=Stained Glass +tile.stainedGlass.black.name=Black Stained Glass +tile.stainedGlass.red.name=Red Stained Glass +tile.stainedGlass.green.name=Green Stained Glass +tile.stainedGlass.brown.name=Brown Stained Glass +tile.stainedGlass.blue.name=Blue Stained Glass +tile.stainedGlass.purple.name=Purple Stained Glass +tile.stainedGlass.cyan.name=Cyan Stained Glass +tile.stainedGlass.silver.name=Light Gray Stained Glass +tile.stainedGlass.gray.name=Gray Stained Glass +tile.stainedGlass.pink.name=Pink Stained Glass +tile.stainedGlass.lime.name=Lime Stained Glass +tile.stainedGlass.yellow.name=Yellow Stained Glass +tile.stainedGlass.lightBlue.name=Light Blue Stained Glass +tile.stainedGlass.magenta.name=Magenta Stained Glass +tile.stainedGlass.orange.name=Orange Stained Glass +tile.stainedGlass.white.name=White Stained Glass +tile.thinStainedGlass.name=Stained Glass Pane +tile.thinStainedGlass.black.name=Black Stained Glass Pane +tile.thinStainedGlass.red.name=Red Stained Glass Pane +tile.thinStainedGlass.green.name=Green Stained Glass Pane +tile.thinStainedGlass.brown.name=Brown Stained Glass Pane +tile.thinStainedGlass.blue.name=Blue Stained Glass Pane +tile.thinStainedGlass.purple.name=Purple Stained Glass Pane +tile.thinStainedGlass.cyan.name=Cyan Stained Glass Pane +tile.thinStainedGlass.silver.name=Light Gray Stained Glass Pane +tile.thinStainedGlass.gray.name=Gray Stained Glass Pane +tile.thinStainedGlass.pink.name=Pink Stained Glass Pane +tile.thinStainedGlass.lime.name=Lime Stained Glass Pane +tile.thinStainedGlass.yellow.name=Yellow Stained Glass Pane +tile.thinStainedGlass.lightBlue.name=Light Blue Stained Glass Pane +tile.thinStainedGlass.magenta.name=Magenta Stained Glass Pane +tile.thinStainedGlass.orange.name=Orange Stained Glass Pane +tile.thinStainedGlass.white.name=White Stained Glass Pane +tile.thinGlass.name=Glass Pane +tile.cloth.name=Wool +tile.flower1.dandelion.name=Dandelion +tile.flower2.poppy.name=Poppy +tile.flower2.blueOrchid.name=Blue Orchid +tile.flower2.allium.name=Allium +tile.flower2.houstonia.name=Azure Bluet +tile.flower2.tulipRed.name=Red Tulip +tile.flower2.tulipOrange.name=Orange Tulip +tile.flower2.tulipWhite.name=White Tulip +tile.flower2.tulipPink.name=Pink Tulip +tile.flower2.oxeyeDaisy.name=Oxeye Daisy +tile.doublePlant.sunflower.name=Sunflower +tile.doublePlant.syringa.name=Lilac +tile.doublePlant.grass.name=Double Tallgrass +tile.doublePlant.fern.name=Large Fern +tile.doublePlant.rose.name=Rose Bush +tile.doublePlant.paeonia.name=Peony +tile.mushroom.name=Mushroom +tile.blockGold.name=Block of Gold +tile.blockIron.name=Block of Iron +tile.stoneSlab.stone.name=Stone Slab +tile.stoneSlab.sand.name=Sandstone Slab +tile.stoneSlab.wood.name=Wooden Slab +tile.stoneSlab.cobble.name=Cobblestone Slab +tile.stoneSlab.brick.name=Bricks Slab +tile.stoneSlab.smoothStoneBrick.name=Stone Bricks Slab +tile.stoneSlab.netherBrick.name=Nether Brick Slab +tile.stoneSlab.quartz.name=Quartz Slab +tile.woodSlab.oak.name=Oak Wood Slab +tile.woodSlab.spruce.name=Spruce Wood Slab +tile.woodSlab.birch.name=Birch Wood Slab +tile.woodSlab.jungle.name=Jungle Wood Slab +tile.woodSlab.acacia.name=Acacia Wood Slab +tile.woodSlab.big_oak.name=Dark Oak Wood Slab +tile.brick.name=Bricks +tile.tnt.name=TNT +tile.bookshelf.name=Bookshelf +tile.stoneMoss.name=Moss Stone +tile.obsidian.name=Obsidian +tile.torch.name=Torch +tile.fire.name=Fire +tile.mobSpawner.name=Monster Spawner +tile.stairsWood.name=Oak Wood Stairs +tile.stairsWoodSpruce.name=Spruce Wood Stairs +tile.stairsWoodBirch.name=Birch Wood Stairs +tile.stairsWoodJungle.name=Jungle Wood Stairs +tile.stairsWoodAcacia.name=Acacia Wood Stairs +tile.stairsWoodDarkOak.name=Dark Oak Wood Stairs +tile.chest.name=Chest +tile.chestTrap.name=Trapped Chest +tile.redstoneDust.name=Redstone Dust +tile.oreDiamond.name=Diamond Ore +tile.blockCoal.name=Block of Coal +tile.blockDiamond.name=Block of Diamond +tile.workbench.name=Crafting Table +tile.crops.name=Crops +tile.farmland.name=Farmland +tile.furnace.name=Furnace +tile.sign.name=Sign +tile.doorWood.name=Wooden Door +tile.ladder.name=Ladder +tile.rail.name=Rail +tile.goldenRail.name=Powered Rail +tile.activatorRail.name=Activator Rail +tile.detectorRail.name=Detector Rail +tile.stairsStone.name=Stone Stairs +tile.stairsSandStone.name=Sandstone Stairs +tile.lever.name=Lever +tile.pressurePlate.name=Pressure Plate +tile.weightedPlate_light.name=Weighted Pressure Plate (Light) +tile.weightedPlate_heavy.name=Weighted Pressure Plate (Heavy) +tile.doorIron.name=Iron Door +tile.oreRedstone.name=Redstone Ore +tile.notGate.name=Redstone Torch +tile.button.name=Button +tile.snow.name=Snow +tile.woolCarpet.black.name=Black Carpet +tile.woolCarpet.red.name=Red Carpet +tile.woolCarpet.green.name=Green Carpet +tile.woolCarpet.brown.name=Brown Carpet +tile.woolCarpet.blue.name=Blue Carpet +tile.woolCarpet.purple.name=Purple Carpet +tile.woolCarpet.cyan.name=Cyan Carpet +tile.woolCarpet.silver.name=Light Gray Carpet +tile.woolCarpet.gray.name=Gray Carpet +tile.woolCarpet.pink.name=Pink Carpet +tile.woolCarpet.lime.name=Lime Carpet +tile.woolCarpet.yellow.name=Yellow Carpet +tile.woolCarpet.lightBlue.name=Light Blue Carpet +tile.woolCarpet.magenta.name=Magenta Carpet +tile.woolCarpet.orange.name=Orange Carpet +tile.woolCarpet.white.name=Carpet +tile.ice.name=Ice +tile.icePacked.name=Packed Ice +tile.cactus.name=Cactus +tile.clay.name=Clay +tile.clayHardenedStained.black.name=Black Stained Clay +tile.clayHardenedStained.red.name=Red Stained Clay +tile.clayHardenedStained.green.name=Green Stained Clay +tile.clayHardenedStained.brown.name=Brown Stained Clay +tile.clayHardenedStained.blue.name=Blue Stained Clay +tile.clayHardenedStained.purple.name=Purple Stained Clay +tile.clayHardenedStained.cyan.name=Cyan Stained Clay +tile.clayHardenedStained.silver.name=Light Gray Stained Clay +tile.clayHardenedStained.gray.name=Gray Stained Clay +tile.clayHardenedStained.pink.name=Pink Stained Clay +tile.clayHardenedStained.lime.name=Lime Stained Clay +tile.clayHardenedStained.yellow.name=Yellow Stained Clay +tile.clayHardenedStained.lightBlue.name=Light Blue Stained Clay +tile.clayHardenedStained.magenta.name=Magenta Stained Clay +tile.clayHardenedStained.orange.name=Orange Stained Clay +tile.clayHardenedStained.white.name=White Stained Clay +tile.clayHardened.name=Hardened Clay +tile.reeds.name=Sugar cane +tile.jukebox.name=Jukebox +tile.fence.name=Fence +tile.fenceGate.name=Fence Gate +tile.pumpkin.name=Pumpkin +tile.litpumpkin.name=Jack o'Lantern +tile.hellrock.name=Netherrack +tile.hellsand.name=Soul Sand +tile.lightgem.name=Glowstone +tile.portal.name=Portal +tile.cloth.black.name=Black Wool +tile.cloth.red.name=Red Wool +tile.cloth.green.name=Green Wool +tile.cloth.brown.name=Brown Wool +tile.cloth.blue.name=Blue Wool +tile.cloth.purple.name=Purple Wool +tile.cloth.cyan.name=Cyan Wool +tile.cloth.silver.name=Light Gray Wool +tile.cloth.gray.name=Gray Wool +tile.cloth.pink.name=Pink Wool +tile.cloth.lime.name=Lime Wool +tile.cloth.yellow.name=Yellow Wool +tile.cloth.lightBlue.name=Light Blue Wool +tile.cloth.magenta.name=Magenta Wool +tile.cloth.orange.name=Orange Wool +tile.cloth.white.name=Wool +tile.oreLapis.name=Lapis Lazuli Ore +tile.blockLapis.name=Lapis Lazuli Block +tile.dispenser.name=Dispenser +tile.dropper.name=Dropper +tile.musicBlock.name=Note Block +tile.cake.name=Cake +tile.bed.name=Bed +tile.bed.occupied=This bed is occupied +tile.bed.noSleep=You can only sleep at night +tile.bed.notSafe=You may not rest now, there are monsters nearby +tile.bed.notValid=Your home bed was missing or obstructed +tile.lockedchest.name=Locked chest +tile.trapdoor.name=Trapdoor +tile.web.name=Cobweb +tile.stonebricksmooth.name=Stone Bricks +tile.stonebricksmooth.default.name=Stone Bricks +tile.stonebricksmooth.mossy.name=Mossy Stone Bricks +tile.stonebricksmooth.cracked.name=Cracked Stone Bricks +tile.stonebricksmooth.chiseled.name=Chiseled Stone Bricks +tile.monsterStoneEgg.stone.name=Stone Monster Egg +tile.monsterStoneEgg.cobble.name=Cobblestone Monster Egg +tile.monsterStoneEgg.brick.name=Stone Brick Monster Egg +tile.monsterStoneEgg.mossybrick.name=Mossy Stone Brick Monster Egg +tile.monsterStoneEgg.crackedbrick.name=Cracked Stone Brick Monster Egg +tile.monsterStoneEgg.chiseledbrick.name=Chiseled Stone Brick Monster Egg +tile.pistonBase.name=Piston +tile.pistonStickyBase.name=Sticky Piston +tile.fenceIron.name=Iron Bars +tile.melon.name=Melon +tile.stairsBrick.name=Brick Stairs +tile.stairsStoneBrickSmooth.name=Stone Brick Stairs +tile.vine.name=Vines +tile.netherBrick.name=Nether Brick +tile.netherFence.name=Nether Brick Fence +tile.stairsNetherBrick.name=Nether Brick Stairs +tile.netherStalk.name=Nether Wart +tile.cauldron.name=Cauldron +tile.enchantmentTable.name=Enchantment Table +tile.anvil.name=Anvil +tile.anvil.intact.name=Anvil +tile.anvil.slightlyDamaged.name=Slightly Damaged Anvil +tile.anvil.veryDamaged.name=Very Damaged Anvil +tile.whiteStone.name=End Stone +tile.endPortalFrame.name=End Portal +tile.mycel.name=Mycelium +tile.waterlily.name=Lily Pad +tile.dragonEgg.name=Dragon Egg +tile.redstoneLight.name=Redstone Lamp +tile.cocoa.name=Cocoa +tile.enderChest.name=Ender Chest +tile.oreRuby.name=Ruby Ore +tile.oreEmerald.name=Emerald Ore +tile.blockEmerald.name=Block of Emerald +tile.blockRedstone.name=Block of Redstone +tile.tripWire.name=Tripwire +tile.tripWireSource.name=Tripwire Hook +tile.commandBlock.name=Command Block +tile.beacon.name=Beacon +tile.beacon.primary=Primary Power +tile.beacon.secondary=Secondary Power +tile.cobbleWall.normal.name=Cobblestone Wall +tile.cobbleWall.mossy.name=Mossy Cobblestone Wall +tile.carrots.name=Carrots +tile.potatoes.name=Potatoes +tile.daylightDetector.name=Daylight Sensor +tile.netherquartz.name=Nether Quartz Ore +tile.hopper.name=Hopper +tile.quartzBlock.default.name=Block of Quartz +tile.quartzBlock.chiseled.name=Chiseled Quartz Block +tile.quartzBlock.lines.name=Pillar Quartz Block +tile.stairsQuartz.name=Quartz Stairs + +item.nameTag.name=Name Tag +item.leash.name=Lead +item.shovelIron.name=Iron Shovel +item.pickaxeIron.name=Iron Pickaxe +item.hatchetIron.name=Iron Axe +item.flintAndSteel.name=Flint and Steel +item.apple.name=Apple +item.cookie.name=Cookie +item.bow.name=Bow +item.arrow.name=Arrow +item.coal.name=Coal +item.charcoal.name=Charcoal +item.diamond.name=Diamond +item.emerald.name=Emerald +item.ingotIron.name=Iron Ingot +item.ingotGold.name=Gold Ingot +item.swordIron.name=Iron Sword +item.swordWood.name=Wooden Sword +item.shovelWood.name=Wooden Shovel +item.pickaxeWood.name=Wooden Pickaxe +item.hatchetWood.name=Wooden Axe +item.swordStone.name=Stone Sword +item.shovelStone.name=Stone Shovel +item.pickaxeStone.name=Stone Pickaxe +item.hatchetStone.name=Stone Axe +item.swordDiamond.name=Diamond Sword +item.shovelDiamond.name=Diamond Shovel +item.pickaxeDiamond.name=Diamond Pickaxe +item.hatchetDiamond.name=Diamond Axe +item.stick.name=Stick +item.bowl.name=Bowl +item.mushroomStew.name=Mushroom Stew +item.swordGold.name=Golden Sword +item.shovelGold.name=Golden Shovel +item.pickaxeGold.name=Golden Pickaxe +item.hatchetGold.name=Golden Axe +item.string.name=String +item.feather.name=Feather +item.sulphur.name=Gunpowder +item.hoeWood.name=Wooden Hoe +item.hoeStone.name=Stone Hoe +item.hoeIron.name=Iron Hoe +item.hoeDiamond.name=Diamond Hoe +item.hoeGold.name=Golden Hoe +item.seeds.name=Seeds +item.seeds_pumpkin.name=Pumpkin Seeds +item.seeds_melon.name=Melon Seeds +item.melon.name=Melon +item.wheat.name=Wheat +item.bread.name=Bread +item.helmetCloth.name=Leather Cap +item.chestplateCloth.name=Leather Tunic +item.leggingsCloth.name=Leather Pants +item.bootsCloth.name=Leather Boots +item.helmetChain.name=Chain Helmet +item.chestplateChain.name=Chain Chestplate +item.leggingsChain.name=Chain Leggings +item.bootsChain.name=Chain Boots +item.helmetIron.name=Iron Helmet +item.chestplateIron.name=Iron Chestplate +item.leggingsIron.name=Iron Leggings +item.bootsIron.name=Iron Boots +item.helmetDiamond.name=Diamond Helmet +item.chestplateDiamond.name=Diamond Chestplate +item.leggingsDiamond.name=Diamond Leggings +item.bootsDiamond.name=Diamond Boots +item.helmetGold.name=Golden Helmet +item.chestplateGold.name=Golden Chestplate +item.leggingsGold.name=Golden Leggings +item.bootsGold.name=Golden Boots +item.flint.name=Flint +item.porkchopRaw.name=Raw Porkchop +item.porkchopCooked.name=Cooked Porkchop +item.chickenRaw.name=Raw Chicken +item.chickenCooked.name=Cooked Chicken +item.beefRaw.name=Raw Beef +item.beefCooked.name=Steak +item.painting.name=Painting +item.frame.name=Item Frame +item.appleGold.name=Golden Apple +item.sign.name=Sign +item.doorWood.name=Wooden Door +item.bucket.name=Bucket +item.bucketWater.name=Water Bucket +item.bucketLava.name=Lava Bucket +item.minecart.name=Minecart +item.saddle.name=Saddle +item.doorIron.name=Iron Door +item.redstone.name=Redstone +item.snowball.name=Snowball +item.boat.name=Boat +item.leather.name=Leather +item.milk.name=Milk +item.brick.name=Brick +item.clay.name=Clay +item.reeds.name=Sugar Canes +item.paper.name=Paper +item.book.name=Book +item.slimeball.name=Slimeball +item.minecartChest.name=Minecart with Chest +item.minecartFurnace.name=Minecart with Furnace +item.minecartTnt.name=Minecart with TNT +item.minecartHopper.name=Minecart with Hopper +item.minecartCommandBlock.name=Minecart with Command Block +item.egg.name=Egg +item.compass.name=Compass +item.fishingRod.name=Fishing Rod +item.clock.name=Clock +item.yellowDust.name=Glowstone Dust +item.fish.cod.raw.name=Raw Fish +item.fish.salmon.raw.name=Raw Salmon +item.fish.pufferfish.raw.name=Pufferfish +item.fish.clownfish.raw.name=Clownfish +item.fish.cod.cooked.name=Cooked Fish +item.fish.salmon.cooked.name=Cooked Salmon +item.record.name=Music Disc +item.record.13.desc=C418 - 13 +item.record.cat.desc=C418 - cat +item.record.blocks.desc=C418 - blocks +item.record.chirp.desc=C418 - chirp +item.record.far.desc=C418 - far +item.record.mall.desc=C418 - mall +item.record.mellohi.desc=C418 - mellohi +item.record.stal.desc=C418 - stal +item.record.strad.desc=C418 - strad +item.record.ward.desc=C418 - ward +item.record.11.desc=C418 - 11 +item.record.wait.desc=C418 - wait +item.bone.name=Bone +item.dyePowder.black.name=Ink Sac +item.dyePowder.red.name=Rose Red +item.dyePowder.green.name=Cactus Green +item.dyePowder.brown.name=Cocoa Beans +item.dyePowder.blue.name=Lapis Lazuli +item.dyePowder.purple.name=Purple Dye +item.dyePowder.cyan.name=Cyan Dye +item.dyePowder.silver.name=Light Gray Dye +item.dyePowder.gray.name=Gray Dye +item.dyePowder.pink.name=Pink Dye +item.dyePowder.lime.name=Lime Dye +item.dyePowder.yellow.name=Dandelion Yellow +item.dyePowder.lightBlue.name=Light Blue Dye +item.dyePowder.magenta.name=Magenta Dye +item.dyePowder.orange.name=Orange Dye +item.dyePowder.white.name=Bone Meal +item.sugar.name=Sugar +item.cake.name=Cake +item.bed.name=Bed +item.diode.name=Redstone Repeater +item.comparator.name=Redstone Comparator +item.map.name=Map +item.leaves.name=Leaves +item.shears.name=Shears +item.rottenFlesh.name=Rotten Flesh +item.enderPearl.name=Ender Pearl +item.blazeRod.name=Blaze Rod +item.ghastTear.name=Ghast Tear +item.netherStalkSeeds.name=Nether Wart +item.potion.name=Potion +item.emptyPotion.name=Water Bottle +item.goldNugget.name=Gold Nugget +item.glassBottle.name=Glass Bottle +item.spiderEye.name=Spider Eye +item.fermentedSpiderEye.name=Fermented Spider Eye +item.blazePowder.name=Blaze Powder +item.magmaCream.name=Magma Cream +item.cauldron.name=Cauldron +item.brewingStand.name=Brewing Stand +item.eyeOfEnder.name=Eye of Ender +item.speckledMelon.name=Glistering Melon +item.monsterPlacer.name=Spawn +item.expBottle.name=Bottle o' Enchanting +item.fireball.name=Fire Charge +item.writingBook.name=Book and Quill +item.writtenBook.name=Written Book +item.ruby.name=Ruby +item.flowerPot.name=Flower Pot +item.emptyMap.name=Empty Map +item.carrots.name=Carrot +item.carrotGolden.name=Golden Carrot +item.potato.name=Potato +item.potatoBaked.name=Baked Potato +item.potatoPoisonous.name=Poisonous Potato +item.skull.skeleton.name=Skeleton Skull +item.skull.wither.name=Wither Skeleton Skull +item.skull.zombie.name=Zombie Head +item.skull.char.name=Head +item.skull.player.name=%s's Head +item.skull.creeper.name=Creeper Head +item.carrotOnAStick.name=Carrot on a Stick +item.netherStar.name=Nether Star +item.pumpkinPie.name=Pumpkin Pie +item.enchantedBook.name=Enchanted Book +item.fireworks.name=Firework Rocket +item.fireworks.flight=Flight Duration: +item.fireworksCharge.name=Firework Star +item.fireworksCharge.black=Black +item.fireworksCharge.red=Red +item.fireworksCharge.green=Green +item.fireworksCharge.brown=Brown +item.fireworksCharge.blue=Blue +item.fireworksCharge.purple=Purple +item.fireworksCharge.cyan=Cyan +item.fireworksCharge.silver=Light Gray +item.fireworksCharge.gray=Gray +item.fireworksCharge.pink=Pink +item.fireworksCharge.lime=Lime +item.fireworksCharge.yellow=Yellow +item.fireworksCharge.lightBlue=Light Blue +item.fireworksCharge.magenta=Magenta +item.fireworksCharge.orange=Orange +item.fireworksCharge.white=White +item.fireworksCharge.customColor=Custom +item.fireworksCharge.fadeTo=Fade to +item.fireworksCharge.flicker=Twinkle +item.fireworksCharge.trail=Trail +item.fireworksCharge.type.0=Small Ball +item.fireworksCharge.type.1=Large Ball +item.fireworksCharge.type.2=Star-shaped +item.fireworksCharge.type.3=Creeper-shaped +item.fireworksCharge.type.4=Burst +item.fireworksCharge.type=Unknown Shape +item.netherbrick.name=Nether Brick +item.netherquartz.name=Nether Quartz +item.horsearmormetal.name=Iron Horse Armor +item.horsearmorgold.name=Gold Horse Armor +item.horsearmordiamond.name=Diamond Horse Armor + +container.inventory=Inventory +container.hopper=Item Hopper +container.crafting=Crafting +container.dispenser=Dispenser +container.dropper=Dropper +container.furnace=Furnace +container.enchant=Enchant +container.repair=Repair & Name +container.repair.cost=Enchantment Cost: %1$d +container.repair.expensive=Too Expensive! +container.creative=Item Selection +container.brewing=Brewing Stand +container.chest=Chest +container.chestDouble=Large Chest +container.minecart=Minecart +container.enderchest=Ender Chest + +item.dyed=Dyed +item.unbreakable=Unbreakable + +entity.Item.name=Item +entity.XPOrb.name=Experience Orb +entity.SmallFireball.name=Small Fireball +entity.Fireball.name=Fireball + +entity.Arrow.name=Arrow +entity.Snowball.name=Snowball +entity.Painting.name=Painting + +entity.Mob.name=Mob +entity.Monster.name=Monster + +entity.Creeper.name=Creeper +entity.Skeleton.name=Skeleton +entity.Spider.name=Spider +entity.Giant.name=Giant +entity.Zombie.name=Zombie +entity.Slime.name=Slime +entity.Ghast.name=Ghast +entity.PigZombie.name=Zombie Pigman +entity.Enderman.name=Enderman +entity.Silverfish.name=Silverfish +entity.CaveSpider.name=Cave Spider +entity.Blaze.name=Blaze +entity.LavaSlime.name=Magma Cube +entity.MushroomCow.name=Mooshroom +entity.Villager.name=Villager +entity.VillagerGolem.name=Iron Golem +entity.SnowMan.name=Snow Golem +entity.EnderDragon.name=Ender Dragon +entity.WitherBoss.name=Wither +entity.Witch.name=Witch + +entity.Pig.name=Pig +entity.Sheep.name=Sheep +entity.Cow.name=Cow +entity.Chicken.name=Chicken +entity.Squid.name=Squid +entity.Wolf.name=Wolf +entity.Ozelot.name=Ocelot +entity.Cat.name=Cat +entity.Bat.name=Bat +entity.EntityHorse.name=Horse +entity.horse.name=Horse +entity.donkey.name=Donkey +entity.mule.name=Mule +entity.skeletonhorse.name=Skeleton Horse +entity.zombiehorse.name=Zombie Horse + +entity.PrimedTnt.name=Block of TNT +entity.FallingSand.name=Falling Block + +entity.Minecart.name=Minecart +entity.Boat.name=Boat + +entity.Arrow.name=arrow +entity.generic.name=unknown + +death.fell.accident.ladder=%1$s fell off a ladder +death.fell.accident.vines=%1$s fell off some vines +death.fell.accident.water=%1$s fell out of the water +death.fell.accident.generic=%1$s fell from a high place +death.fell.killer=%1$s was doomed to fall +death.fell.assist=%1$s was doomed to fall by %2$s +death.fell.assist.item=%1$s was doomed to fall by %2$s using %3$s +death.fell.finish=%1$s fell too far and was finished by %2$s +death.fell.finish.item=%1$s fell too far and was finished by %2$s using %3$s + +death.attack.inFire=%1$s went up in flames +death.attack.inFire.player=%1$s walked into fire whilst fighting %2$s +death.attack.onFire=%1$s burned to death +death.attack.onFire.player=%1$s was burnt to a crisp whilst fighting %2$s +death.attack.lava=%1$s tried to swim in lava +death.attack.lava.player=%1$s tried to swim in lava to escape %2$s +death.attack.inWall=%1$s suffocated in a wall +death.attack.drown=%1$s drowned +death.attack.drown.player=%1$s drowned whilst trying to escape %2$s +death.attack.starve=%1$s starved to death +death.attack.cactus=%1$s was pricked to death +death.attack.cactus.player=%1$s walked into a cactus whilst trying to escape %2$s +death.attack.generic=%1$s died +death.attack.explosion=%1$s blew up +death.attack.explosion.player=%1$s was blown up by %2$s +death.attack.magic=%1$s was killed by magic +death.attack.wither=%1$s withered away +death.attack.anvil=%1$s was squashed by a falling anvil +death.attack.fallingBlock=%1$s was squashed by a falling block +death.attack.mob=%1$s was slain by %2$s +death.attack.player=%1$s was slain by %2$s +death.attack.player.item=%1$s was slain by %2$s using %3$s +death.attack.arrow=%1$s was shot by %2$s +death.attack.arrow.item=%1$s was shot by %2$s using %3$s +death.attack.fireball=%1$s was fireballed by %2$s +death.attack.fireball.item=%1$s was fireballed by %2$s using %3$s +death.attack.thrown=%1$s was pummeled by %2$s +death.attack.thrown.item=%1$s was pummeled by %2$s using %3$s +death.attack.indirectMagic=%1$s was killed by %2$s using magic +death.attack.indirectMagic.item=%1$s was killed by %2$s using %3$s +death.attack.thorns=%1$s was killed trying to hurt %2$s +death.attack.fall=%1$s hit the ground too hard +death.attack.outOfWorld=%1$s fell out of the world + +deathScreen.respawn=Respawn +deathScreen.deleteWorld=Delete world +deathScreen.titleScreen=Title screen +deathScreen.score=Score +deathScreen.title.hardcore=Game over! +deathScreen.hardcoreInfo=You cannot respawn in hardcore mode! +deathScreen.title=You died! +deathScreen.leaveServer=Leave server +deathScreen.quit.confirm=Are you sure you want to quit? + +potion.effects.whenDrank=When Applied: +potion.empty=No Effects +potion.moveSpeed=Speed +potion.moveSlowdown=Slowness +potion.digSpeed=Haste +potion.digSlowDown=Mining Fatigue +potion.damageBoost=Strength +potion.weakness=Weakness +potion.heal=Instant Health +potion.harm=Instant Damage +potion.jump=Jump Boost +potion.confusion=Nausea +potion.regeneration=Regeneration +potion.resistance=Resistance +potion.fireResistance=Fire Resistance +potion.waterBreathing=Water Breathing +potion.invisibility=Invisibility +potion.blindness=Blindness +potion.nightVision=Night Vision +potion.hunger=Hunger +potion.poison=Poison +potion.wither=Wither +potion.healthBoost=Health Boost +potion.absorption=Absorption +potion.saturation=Saturation + +potion.moveSpeed.postfix=Potion of Swiftness +potion.moveSlowdown.postfix=Potion of Slowness +potion.digSpeed.postfix=Potion of Haste +potion.digSlowDown.postfix=Potion of Dullness +potion.damageBoost.postfix=Potion of Strength +potion.weakness.postfix=Potion of Weakness +potion.heal.postfix=Potion of Healing +potion.harm.postfix=Potion of Harming +potion.jump.postfix=Potion of Leaping +potion.confusion.postfix=Potion of Nausea +potion.regeneration.postfix=Potion of Regeneration +potion.resistance.postfix=Potion of Resistance +potion.fireResistance.postfix=Potion of Fire Resistance +potion.waterBreathing.postfix=Potion of Water Breathing +potion.invisibility.postfix=Potion of Invisibility +potion.blindness.postfix=Potion of Blindness +potion.nightVision.postfix=Potion of Night Vision +potion.hunger.postfix=Potion of Hunger +potion.poison.postfix=Potion of Poison +potion.wither.postfix=Potion of Decay +potion.healthBoost.postfix=Potion of Health Boost +potion.absorption.postfix=Potion of Absorption +potion.saturation.postfix=Potion of Saturation + +potion.potency.0= +potion.potency.1=II +potion.potency.2=III +potion.potency.3=IV + +potion.prefix.grenade=Splash +potion.prefix.mundane=Mundane +potion.prefix.uninteresting=Uninteresting +potion.prefix.bland=Bland +potion.prefix.clear=Clear +potion.prefix.milky=Milky +potion.prefix.diffuse=Diffuse +potion.prefix.artless=Artless +potion.prefix.thin=Thin +potion.prefix.awkward=Awkward +potion.prefix.flat=Flat +potion.prefix.bulky=Bulky +potion.prefix.bungling=Bungling +potion.prefix.buttered=Buttered +potion.prefix.smooth=Smooth +potion.prefix.suave=Suave +potion.prefix.debonair=Debonair +potion.prefix.thick=Thick +potion.prefix.elegant=Elegant +potion.prefix.fancy=Fancy +potion.prefix.charming=Charming +potion.prefix.dashing=Dashing +potion.prefix.refined=Refined +potion.prefix.cordial=Cordial +potion.prefix.sparkling=Sparkling +potion.prefix.potent=Potent +potion.prefix.foul=Foul +potion.prefix.odorless=Odorless +potion.prefix.rank=Rank +potion.prefix.harsh=Harsh +potion.prefix.acrid=Acrid +potion.prefix.gross=Gross +potion.prefix.stinky=Stinky + +enchantment.damage.all=Sharpness +enchantment.damage.undead=Smite +enchantment.damage.arthropods=Bane of Arthropods +enchantment.knockback=Knockback +enchantment.fire=Fire Aspect +enchantment.protect.all=Protection +enchantment.protect.fire=Fire Protection +enchantment.protect.fall=Feather Falling +enchantment.protect.explosion=Blast Protection +enchantment.protect.projectile=Projectile Protection +enchantment.oxygen=Respiration +enchantment.waterWorker=Aqua Affinity +enchantment.digging=Efficiency +enchantment.untouching=Silk Touch +enchantment.durability=Unbreaking +enchantment.lootBonus=Looting +enchantment.lootBonusDigger=Fortune +enchantment.lootBonusFishing=Luck of the Sea +enchantment.fishingSpeed=Lure +enchantment.arrowDamage=Power +enchantment.arrowFire=Flame +enchantment.arrowKnockback=Punch +enchantment.arrowInfinite=Infinity +enchantment.thorns=Thorns + +enchantment.level.1=I +enchantment.level.2=II +enchantment.level.3=III +enchantment.level.4=IV +enchantment.level.5=V +enchantment.level.6=VI +enchantment.level.7=VII +enchantment.level.8=VIII +enchantment.level.9=IX +enchantment.level.10=X + +gui.achievements=Achievements +gui.stats=Statistics + +stats.tooltip.type.achievement=Achievement +stats.tooltip.type.statistic=Statistic +stat.generalButton=General +stat.blocksButton=Blocks +stat.itemsButton=Items +stat.mobsButton=Mobs + +stat.used=Times Used +stat.mined=Times Mined +stat.depleted=Times Depleted +stat.crafted=Times Crafted +stat.entityKills=You killed %d %s +stat.entityKilledBy=%s killed you %d time(s) +stat.entityKills.none=You have never killed %s +stat.entityKilledBy.none=You have never been killed by %s + +stat.startGame=Times played +stat.createWorld=Worlds created +stat.loadWorld=Saves loaded +stat.joinMultiplayer=Multiplayer joins +stat.leaveGame=Games quit + +stat.playOneMinute=Minutes Played + +stat.walkOneCm=Distance Walked +stat.fallOneCm=Distance Fallen +stat.swimOneCm=Distance Swum +stat.flyOneCm=Distance Flown +stat.climbOneCm=Distance Climbed +stat.diveOneCm=Distance Dove +stat.minecartOneCm=Distance by Minecart +stat.boatOneCm=Distance by Boat +stat.pigOneCm=Distance by Pig +stat.horseOneCm=Distance by Horse +stat.jump=Jumps +stat.drop=Items Dropped + +stat.damageDealt=Damage Dealt +stat.damageTaken=Damage Taken +stat.deaths=Number of Deaths +stat.mobKills=Mob Kills +stat.animalsBred=Animals Bred +stat.playerKills=Player Kills +stat.fishCaught=Fish Caught +stat.treasureFished=Treasure Fished +stat.junkFished=Junk Fished + +stat.mineBlock=%1$s Mined +stat.craftItem=%1$s Crafted +stat.useItem=%1$s Used +stat.breakItem=%1$s Depleted + +achievement.get=Achievement get! + +achievement.taken=Taken! +achievement.unknown=??? + +achievement.requires=Requires '%1$s' +achievement.openInventory=Taking Inventory +achievement.openInventory.desc=Press '%1$s' to open your inventory. +achievement.mineWood=Getting Wood +achievement.mineWood.desc=Attack a tree until a block of wood pops out +achievement.buildWorkBench=Benchmarking +achievement.buildWorkBench.desc=Craft a workbench with four blocks of planks +achievement.buildPickaxe=Time to Mine! +achievement.buildPickaxe.desc=Use planks and sticks to make a pickaxe +achievement.buildFurnace=Hot Topic +achievement.buildFurnace.desc=Construct a furnace out of eight stone blocks +achievement.acquireIron=Acquire Hardware +achievement.acquireIron.desc=Smelt an iron ingot +achievement.buildHoe=Time to Farm! +achievement.buildHoe.desc=Use planks and sticks to make a hoe +achievement.makeBread=Bake Bread +achievement.makeBread.desc=Turn wheat into bread +achievement.bakeCake=The Lie +achievement.bakeCake.desc=Wheat, sugar, milk and eggs! +achievement.buildBetterPickaxe=Getting an Upgrade +achievement.buildBetterPickaxe.desc=Construct a better pickaxe +achievement.cookFish=Delicious Fish +achievement.cookFish.desc=Catch and cook fish! +achievement.onARail=On A Rail +achievement.onARail.desc=Travel by minecart at least 1 km from where you started +achievement.buildSword=Time to Strike! +achievement.buildSword.desc=Use planks and sticks to make a sword +achievement.killEnemy=Monster Hunter +achievement.killEnemy.desc=Attack and destroy a monster +achievement.killCow=Cow Tipper +achievement.killCow.desc=Harvest some leather +achievement.breedCow=Repopulation +achievement.breedCow.desc=Breed two cows with wheat +achievement.flyPig=When Pigs Fly +achievement.flyPig.desc=Fly a pig off a cliff +achievement.snipeSkeleton=Sniper Duel +achievement.snipeSkeleton.desc=Kill a skeleton with an arrow from more than 50 meters +achievement.diamonds=DIAMONDS! +achievement.diamonds.desc=Acquire diamonds with your iron tools +achievement.diamondsToYou=Diamonds to you! +achievement.diamondsToYou.desc=Throw diamonds at another player. +achievement.portal=We Need to Go Deeper +achievement.portal.desc=Build a portal to the Nether +achievement.ghast=Return to Sender +achievement.ghast.desc=Destroy a Ghast with a fireball +achievement.blazeRod=Into Fire +achievement.blazeRod.desc=Relieve a Blaze of its rod +achievement.potion=Local Brewery +achievement.potion.desc=Brew a potion +achievement.theEnd=The End? +achievement.theEnd.desc=Locate the End +achievement.theEnd2=The End. +achievement.theEnd2.desc=Defeat the Ender Dragon +achievement.spawnWither=The Beginning? +achievement.spawnWither.desc=Spawn the Wither +achievement.killWither=The Beginning. +achievement.killWither.desc=Kill the Wither +achievement.fullBeacon=Beaconator +achievement.fullBeacon.desc=Create a full beacon +achievement.exploreAllBiomes=Adventuring Time +achievement.exploreAllBiomes.desc=Discover all biomes +achievement.enchantments=Enchanter +achievement.enchantments.desc=Use a book, obsidian and diamonds to construct an enchantment table +achievement.overkill=Overkill +achievement.overkill.desc=Deal eight hearts of damage in a single hit +achievement.bookcase=Librarian +achievement.bookcase.desc=Build some bookshelves to improve your enchantment table + +commands.generic.exception=An unknown error occurred while attempting to perform this command +commands.generic.permission=You do not have permission to use this command +commands.generic.syntax=Invalid command syntax +commands.generic.player.notFound=That player cannot be found +commands.generic.notFound=Unknown command. Try /help for a list of commands +commands.generic.num.invalid='%s' is not a valid number +commands.generic.boolean.invalid='%s' is not true or false +commands.generic.num.tooSmall=The number you have entered (%d) is too small, it must be at least %d +commands.generic.num.tooBig=The number you have entered (%d) is too big, it must be at most %d +commands.generic.double.tooSmall=The number you have entered (%.2f) is too small, it must be at least %.2f +commands.generic.double.tooBig=The number you have entered (%.2f) is too big, it must be at most %.2f +commands.generic.usage=Usage: %s +commands.generic.deprecatedId=Warning: Using numeric IDs will not be supported in the future. Please use names, such as '%s' + +commands.setidletimeout.usage=/setidletimeout +commands.setidletimeout.success=Successfully set the idle timeout to %d minutes. +commands.xp.failure.widthdrawXp=Cannot give player negative experience points +commands.xp.success=Given %d experience to %s +commands.xp.success.levels=Given %d levels to %s +commands.xp.success.negative.levels=Taken %d levels from %s +commands.xp.usage=/xp [player] OR /xp L [player] +commands.playsound.usage=/playsound [x] [y] [z] [volume] [pitch] [minimumVolume] +commands.playsound.success=Played sound '%s' to %s +commands.playsound.playerTooFar=Player %s is too far away to hear the sound +commands.give.usage=/give [amount] [data] [dataTag] +commands.give.notFound=There is no such item with ID %d +commands.give.success=Given %s * %d to %s +commands.give.tagError=Data tag parsing failed: %s +commands.summon.usage=/summon [x] [y] [z] [dataTag] +commands.summon.success=Object successfully summoned +commands.summon.failed=Unable to summon object +commands.summon.tagError=Data tag parsing failed: %s +commands.summon.outOfWorld=Cannot summon the object out of the world +commands.testforblock.usage=/testforblock [dataValue] [dataTag] +commands.testforblock.failed.tile=The block at %d,%d,%d is %s (expected: %s). +commands.testforblock.failed.data=The block at %d,%d,%d had the data value of %s (expected: %s). +commands.testforblock.failed.nbt=The block at %d,%d,%d did not have the required NBT keys. +commands.testforblock.failed.tileEntity=The block at %d,%d,%d is not a tile entity and cannot support tag matching. +commands.testforblock.success=Successfully found the block at %d,%d,%d. +commands.testforblock.outOfWorld=Cannot test for block outside of the world +commands.setblock.usage=/setblock [dataValue] [oldBlockHandling] [dataTag] +commands.setblock.success=Block placed +commands.setblock.failed=Unable to place block +commands.setblock.tagError=Data tag parsing failed: %s +commands.setblock.outOfWorld=Cannot place block outside of the world +commands.setblock.notFound=There is no such block with ID/name %s +commands.setblock.noChange=The block couldn't be placed +commands.effect.usage=/effect [seconds] [amplifier] +commands.effect.notFound=There is no such mob effect with ID %d +commands.effect.success=Given %1$s (ID %2$d) * %3$d to %4$s for %5$d seconds +commands.effect.success.removed=Took %1$s from %2$s +commands.effect.success.removed.all=Took all effects from %s +commands.effect.failure.notActive=Couldn't take %1$s from %2$s as they do not have the effect +commands.effect.failure.notActive.all=Couldn't take any effects from %s as they do not have any +commands.enchant.usage=/enchant [level] +commands.enchant.notFound=There is no such enchantment with ID %d +commands.enchant.noItem=The target doesn't hold an item +commands.enchant.cantEnchant=The selected enchantment can't be added to the target item +commands.enchant.cantCombine=%1$s can't be combined with %2$s +commands.enchant.success=Enchanting succeeded +commands.clear.usage=/clear [item] [data] +commands.clear.success=Cleared the inventory of %s, removing %d items +commands.clear.failure=Could not clear the inventory of %s, no items to remove +commands.downfall.usage=/toggledownfall +commands.downfall.success=Toggled downfall +commands.time.usage=/time +commands.time.added=Added %d to the time +commands.time.set=Set the time to %d +commands.players.usage=/list +commands.players.list=There are %d/%d players online: +commands.banlist.ips=There are %d total banned IP addresses: +commands.banlist.players=There are %d total banned players: +commands.banlist.usage=/banlist [ips|players] +commands.kill.usage=/kill +commands.kill.success=Ouch! That looked like it hurt +commands.kick.success=Kicked %s from the game +commands.kick.success.reason=Kicked %s from the game: '%s' +commands.kick.usage=/kick [reason ...] +commands.op.success=Opped %s +commands.op.usage=/op +commands.deop.success=De-opped %s +commands.deop.usage=/deop +commands.say.usage=/say +commands.ban.success=Banned player %s +commands.ban.usage=/ban [reason ...] +commands.unban.success=Unbanned player %s +commands.unban.usage=/pardon +commands.banip.invalid=You have entered an invalid IP address or a player that is not online +commands.banip.success=Banned IP address %s +commands.banip.success.players=Banned IP address %s belonging to %s +commands.banip.usage=/ban-ip [reason ...] +commands.unbanip.invalid=You have entered an invalid IP address +commands.unbanip.success=Unbanned IP address %s +commands.unbanip.usage=/pardon-ip
+commands.save.usage=/save-all +commands.save-on.alreadyOn=Saving is already turned on. +commands.save-on.usage=/save-on +commands.save-off.alreadyOff=Saving is already turned off. +commands.save-off.usage=/save-off +commands.save.enabled=Turned on world auto-saving +commands.save.disabled=Turned off world auto-saving +commands.save.start=Saving... +commands.save.success=Saved the world +commands.save.failed=Saving failed: %s +commands.stop.usage=/stop +commands.stop.start=Stopping the server +commands.tp.success=Teleported %s to %s +commands.tp.success.coordinates=Teleported %s to %.2f,%.2f,%.2f +commands.tp.usage=/tp [target player] OR /tp [target player] +commands.tp.notSameDimension=Unable to teleport because players are not in the same dimension +commands.whitelist.list=There are %d (out of %d seen) whitelisted players: +commands.whitelist.enabled=Turned on the whitelist +commands.whitelist.disabled=Turned off the whitelist +commands.whitelist.reloaded=Reloaded the whitelist +commands.whitelist.add.success=Added %s to the whitelist +commands.whitelist.add.usage=/whitelist add +commands.whitelist.remove.success=Removed %s from the whitelist +commands.whitelist.remove.usage=/whitelist remove +commands.whitelist.usage=/whitelist +commands.scoreboard.usage=/scoreboard +commands.scoreboard.teamNotFound=No team was found by the name '%s' +commands.scoreboard.objectiveNotFound=No objective was found by the name '%s' +commands.scoreboard.objectiveReadOnly=The objective '%s' is read-only and cannot be set +commands.scoreboard.objectives.usage=/scoreboard objectives +commands.scoreboard.objectives.setdisplay.usage=/scoreboard objectives setdisplay [objective] +commands.scoreboard.objectives.setdisplay.invalidSlot=No such display slot '%s' +commands.scoreboard.objectives.setdisplay.successCleared=Cleared objective display slot '%s' +commands.scoreboard.objectives.setdisplay.successSet=Set the display objective in slot '%s' to '%s' +commands.scoreboard.objectives.add.usage=/scoreboard objectives add [display name ...] +commands.scoreboard.objectives.add.wrongType=Invalid objective criteria type '%s' +commands.scoreboard.objectives.add.alreadyExists=An objective with the name '%s' already exists +commands.scoreboard.objectives.add.tooLong=The name '%s' is too long for an objective, it can be at most %d characters long +commands.scoreboard.objectives.add.displayTooLong=The display name '%s' is too long for an objective, it can be at most %d characters long +commands.scoreboard.objectives.add.success=Added new objective '%s' successfully +commands.scoreboard.objectives.remove.usage=/scoreboard objectives remove +commands.scoreboard.objectives.remove.success=Removed objective '%s' successfully +commands.scoreboard.objectives.list.count=Showing %d objective(s) on scoreboard: +commands.scoreboard.objectives.list.entry=- %s: displays as '%s' and is type '%s' +commands.scoreboard.objectives.list.empty=There are no objectives on the scoreboard +commands.scoreboard.players.usage=/scoreboard players +commands.scoreboard.players.set.success=Set score of %s for player %s to %d +commands.scoreboard.players.set.usage=/scoreboard players set +commands.scoreboard.players.add.usage=/scoreboard players add +commands.scoreboard.players.remove.usage=/scoreboard players remove +commands.scoreboard.players.reset.usage=/scoreboard players reset +commands.scoreboard.players.reset.success=Reset all scores of player %s +commands.scoreboard.players.list.usage=/scoreboard players list [name] +commands.scoreboard.players.list.count=Showing %d tracked players on the scoreboard: +commands.scoreboard.players.list.empty=There are no tracked players on the scoreboard +commands.scoreboard.players.list.player.count=Showing %d tracked objective(s) for %s: +commands.scoreboard.players.list.player.entry=- %2$s: %1$d (%3$s) +commands.scoreboard.players.list.player.empty=Player %s has no scores recorded +commands.scoreboard.teams.usage=/scoreboard teams +commands.scoreboard.teams.add.usage=/scoreboard teams add [display name ...] +commands.scoreboard.teams.add.alreadyExists=A team with the name '%s' already exists +commands.scoreboard.teams.add.tooLong=The name '%s' is too long for a team, it can be at most %d characters long +commands.scoreboard.teams.add.displayTooLong=The display name '%s' is too long for a team, it can be at most %d characters long +commands.scoreboard.teams.add.success=Added new team '%s' successfully +commands.scoreboard.teams.list.usage=/scoreboard teams list [name] +commands.scoreboard.teams.list.count=Showing %d teams on the scoreboard: +commands.scoreboard.teams.list.entry=- %1$s: '%2$s' has %3$d players +commands.scoreboard.teams.list.empty=There are no teams registered on the scoreboard +commands.scoreboard.teams.list.player.count=Showing %d player(s) in team %s: +commands.scoreboard.teams.list.player.entry=- %2$s: %1$d (%3$s) +commands.scoreboard.teams.list.player.empty=Team %s has no players +commands.scoreboard.teams.empty.usage=/scoreboard teams empty +commands.scoreboard.teams.empty.alreadyEmpty=Team %s is already empty, cannot remove nonexistant players +commands.scoreboard.teams.empty.success=Removed all %d player(s) from team %s +commands.scoreboard.teams.remove.usage=/scoreboard teams remove +commands.scoreboard.teams.remove.success=Removed team %s +commands.scoreboard.teams.join.usage=/scoreboard teams join [player] +commands.scoreboard.teams.join.success=Added %d player(s) to team %s: %s +commands.scoreboard.teams.join.failure=Could not add %d player(s) to team %s: %s +commands.scoreboard.teams.leave.usage=/scoreboard teams leave [player] +commands.scoreboard.teams.leave.success=Removed %d player(s) from their teams: %s +commands.scoreboard.teams.leave.failure=Could not remove %d player(s) from their teams: %s +commands.scoreboard.teams.leave.noTeam=You are not in a team +commands.scoreboard.teams.option.usage=/scoreboard teams option +commands.scoreboard.teams.option.noValue=Valid values for option %s are: %s +commands.scoreboard.teams.option.success=Set option %s for team %s to %s +commands.gamemode.success.self=Set own game mode to %s +commands.gamemode.success.other=Set %s's game mode to %s +commands.gamemode.usage=/gamemode [player] +commands.defaultgamemode.usage=/defaultgamemode +commands.defaultgamemode.success=The world's default game mode is now %s +commands.me.usage=/me +commands.help.header=--- Showing help page %d of %d (/help ) --- +commands.help.footer=Tip: Use the key while typing a command to auto-complete the command or its arguments +commands.help.usage=/help [page|command name] +commands.publish.usage=/publish +commands.publish.started=Local game hosted on port %s +commands.publish.failed=Unable to host local game +commands.debug.start=Started debug profiling +commands.debug.stop=Stopped debug profiling after %.2f seconds (%d ticks) +commands.debug.notStarted=Can't stop profiling when we haven't started yet! +commands.debug.usage=/debug +commands.tellraw.usage=/tellraw +commands.tellraw.jsonException=Invalid json: %s +commands.message.usage=/tell +commands.message.sameTarget=You can't send a private message to yourself! +commands.message.display.outgoing=You whisper to %s: %s +commands.message.display.incoming=%s whispers to you: %s +commands.difficulty.usage=/difficulty +commands.difficulty.success=Set game difficulty to %s +commands.spawnpoint.usage=/spawnpoint OR /spawnpoint OR /spawnpoint +commands.spawnpoint.success=Set %s's spawn point to (%d, %d, %d) +commands.setworldspawn.usage=/setworldspawn OR /setworldspawn +commands.setworldspawn.success=Set the world spawn point to (%d, %d, %d) +commands.gamerule.usage=/gamerule OR /gamerule +commands.gamerule.success=Game rule has been updated +commands.gamerule.norule=No game rule called '%s' is available +commands.weather.usage=/weather [duration in seconds] +commands.weather.clear=Changing to clear weather +commands.weather.rain=Changing to rainy weather +commands.weather.thunder=Changing to rain and thunder +commands.testfor.usage=/testfor +commands.testfor.failed=/testfor is only usable by commandblocks with analog output +commands.seed.usage=/seed +commands.seed.success=Seed: %s +commands.spreadplayers.usage=/spreadplayers +commands.spreadplayers.spreading.teams=Spreading %s teams %s blocks around %s,%s (min %s blocks apart) +commands.spreadplayers.spreading.players=Spreading %s players %s blocks around %s,%s (min %s blocks apart) +commands.spreadplayers.success.teams=Successfully spread %s teams around %s,%s +commands.spreadplayers.success.players=Successfully spread %s players around %s,%s +commands.spreadplayers.info.teams=(Average distance between teams is %s blocks apart after %s iterations) +commands.spreadplayers.info.players=(Average distance between players is %s blocks apart after %s iterations) +commands.spreadplayers.failure.teams=Could not spread %s teams around %s,%s (too many players for space - try using spread of at most %s) +commands.spreadplayers.failure.players=Could not spread %s players around %s,%s (too many players for space - try using spread of at most %s) +commands.achievement.usage=/achievement give [player] +commands.achievement.unknownAchievement=Unknown achievement or statistic '%s' +commands.achievement.give.success.all=Successfully given all achievements to %s +commands.achievement.give.success.one=Successfully given %s the stat %s +commands.achievement.statTooLow=Player %s does not have the stat %s + +itemGroup.buildingBlocks=Building Blocks +itemGroup.decorations=Decoration Blocks +itemGroup.redstone=Redstone +itemGroup.transportation=Transportation +itemGroup.misc=Miscellaneous +itemGroup.search=Search Items +itemGroup.food=Foodstuffs +itemGroup.tools=Tools +itemGroup.combat=Combat +itemGroup.brewing=Brewing +itemGroup.materials=Materials +itemGroup.inventory=Survival Inventory + +inventory.binSlot=Destroy Item + +advMode.setCommand=Set Console Command for Block +advMode.setCommand.success=Command set: %s +advMode.command=Console Command +advMode.nearestPlayer=Use "@p" to target nearest player +advMode.randomPlayer=Use "@r" to target random player +advMode.allPlayers=Use "@a" to target all players +advMode.previousOutput=Previous Output + +advMode.notEnabled=Command blocks are not enabled on this server +advMode.notAllowed=Must be an opped player in creative mode + +mco.title=Minecraft Realms +mount.onboard=Press %1$s to dismount + +mco.terms.buttons.agree=Agree +mco.terms.buttons.disagree=Don't Agree +mco.terms.title=Realms Terms of Service +mco.terms.sentence.1=I agree to Minecraft Realms +mco.terms.sentence.2=Terms of Service + +mco.buy.realms.title=Buy a Realm +mco.buy.realms.buy=I want one! + +mco.selectServer.play=Play +mco.selectServer.configure=Configure +mco.selectServer.leave=Leave Realm +mco.selectServer.create=Create Realm +mco.selectServer.buy=Buy Realm +mco.selectServer.moreinfo=More Info + +mco.selectServer.expired=Expired Server +mco.selectServer.open=Open Server +mco.selectServer.closed=Closed Server +mco.selectServer.locked=Locked Server + +mco.selectServer.expires.days=Expires in %s days +mco.selectServer.expires.day=Expires in a day +mco.selectServer.expires.soon=Expires soon + +mco.configure.world.edit.title=Edit Realm + +mco.configure.world.title=Configure Realm +mco.configure.world.name=Name +mco.configure.world.description=Description +mco.configure.world.location=Location +mco.configure.world.invited=Invited +mco.configure.world.buttons.edit=Edit +mco.configure.world.buttons.reset=Reset Realm +mco.configure.world.buttons.done=Done +mco.configure.world.buttons.delete=Delete +mco.configure.world.buttons.open=Open Realm +mco.configure.world.buttons.close=Close Realm +mco.configure.world.buttons.invite=Invite +mco.configure.world.buttons.uninvite=Uninvite +mco.configure.world.buttons.backup=Backups +mco.configure.world.buttons.subscription=Subscription +mco.configure.world.invite.profile.name=Name +mco.configure.world.uninvite.question=Are you sure that you want to uninvite +mco.configure.world.status=Status + +mco.configure.world.subscription.title=Subscription Info +mco.configure.world.subscription.daysleft=Days Left +mco.configure.world.subscription.start=Start Date +mco.configure.world.subscription.extend=Extend Subscription + +mco.create.world.location.title=Locations +mco.create.world.location.warning=You may not get the exact location you select +mco.create.world.wait=Creating the realm... +mco.create.world.seed=Seed (Optional) + +mco.reset.world.title=Reset Realm +mco.reset.world.warning=This will permanently delete your realm! +mco.reset.world.seed=Seed (Optional) +mco.reset.world.resetting.screen.title=Resetting Realm... + +mco.configure.world.close.question.line1=Your realm will become unavailable. +mco.configure.world.close.question.line2=Are you sure you want to do that? + +mco.configure.world.leave.question.line1=If you leave this realm you won't see it unless invited again +mco.configure.world.leave.question.line2=Are you sure you want to do that? + +mco.configure.world.reset.question.line1=Your realm will be regenerated and your current realm will be lost +mco.configure.world.reset.question.line2=Are you sure you want to do that? + +mco.configure.world.restore.question.line1=Your realm will be restored to date +mco.configure.world.restore.question.line2=Are you sure you want to do that? + +mco.configure.world.restore.download.question.line1=You will be redirected to your default browser to download your world map. +mco.configure.world.restore.download.question.line2=Do you want to continue? + +mco.more.info.question.line1=You will be redirected to your default browser to see the page. +mco.more.info.question.line2=Do you want to continue? + +mco.connect.connecting=Connecting to the online server... +mco.connect.authorizing=Logging in... +mco.connect.failed=Failed to connect to the online server + +mco.create.world=Create + +mco.client.outdated.title=Client Outdated! +mco.client.outdated.msg=Your client is outdated, please consider updating it to use Realms + +mco.backup.title=Backups +mco.backup.button.restore=Restore +mco.backup.restoring=Restoring your realm +mco.backup.button.download=Download Latest + +mco.template.title=Realm Templates +mco.template.button.select=Select +mco.template.default.name=Select Template (Optional) +mco.template.name=Template + +mco.invites.button.accept=Accept +mco.invites.button.reject=Reject +mco.invites.title=Pending Invitations +mco.invites.pending=New invitations! +mco.invites.nopending=No pending invitations! + +build.tooHigh=Height limit for building is %s blocks + +attribute.modifier.plus.0=+%d %s +attribute.modifier.plus.1=+%d%% %s +attribute.modifier.plus.2=+%d%% %s +attribute.modifier.take.0=-%d %s +attribute.modifier.take.1=-%d%% %s +attribute.modifier.take.2=-%d%% %s + +attribute.name.horse.jumpStrength=Horse Jump Strength +attribute.name.zombie.spawnReinforcements=Zombie Reinforcements +attribute.name.generic.maxHealth=Max Health +attribute.name.generic.followRange=Mob Follow Range +attribute.name.generic.knockbackResistance=Knockback Resistance +attribute.name.generic.movementSpeed=Speed +attribute.name.generic.attackDamage=Attack Damage + +screenshot.success=Saved screenshot as %s +screenshot.failure=Couldn't save screenshot: %s From c7e590e286dce6fd8693f12510a3c24ae87d03c4 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Fri, 6 Dec 2013 23:21:52 +0000 Subject: [PATCH 08/18] Move en_US.properties to mojang-translations/en_US.properties --- .../java/net/md_5/bungee/api/chat/TranslatableComponent.java | 2 +- .../main/resources/{ => mojang-translations}/en_US.properties | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename proxy/src/main/resources/{ => mojang-translations}/en_US.properties (100%) diff --git a/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java index 7081eb2b..f0ac3e0e 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java @@ -14,7 +14,7 @@ import java.util.ResourceBundle; @NoArgsConstructor public class TranslatableComponent extends BaseComponent { - public final ResourceBundle locales = ResourceBundle.getBundle( "en_US" ); + public final ResourceBundle locales = ResourceBundle.getBundle( "mojang-translations/en_US" ); private String translate; private List with; diff --git a/proxy/src/main/resources/en_US.properties b/proxy/src/main/resources/mojang-translations/en_US.properties similarity index 100% rename from proxy/src/main/resources/en_US.properties rename to proxy/src/main/resources/mojang-translations/en_US.properties From 2cb3b6f934d0951c3dde66bdccca8b6505869168 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Sat, 7 Dec 2013 13:16:10 +0000 Subject: [PATCH 09/18] Fix TranslatableComponent's toPlainText and toLegacyText not handling %1$s and %d --- .../api/chat/TranslatableComponent.java | 105 +++++++++++++----- 1 file changed, 78 insertions(+), 27 deletions(-) diff --git a/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java index f0ac3e0e..2730aa2a 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java @@ -6,8 +6,11 @@ import lombok.Setter; import net.md_5.bungee.api.ChatColor; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.ResourceBundle; +import java.util.regex.Matcher; +import java.util.regex.Pattern; @Getter @Setter @@ -15,6 +18,7 @@ import java.util.ResourceBundle; public class TranslatableComponent extends BaseComponent { public final ResourceBundle locales = ResourceBundle.getBundle( "mojang-translations/en_US" ); + private final Pattern format = Pattern.compile( "%(?:(\\d+)\\$)?([A-Za-z%]|$)" ); private String translate; private List with; @@ -22,17 +26,18 @@ public class TranslatableComponent extends BaseComponent public TranslatableComponent(String translate, Object... with) { setTranslate( translate ); - this.with = new ArrayList<>(); + List temp = new ArrayList<>(); for ( Object w : with ) { if ( w instanceof String ) { - this.with.add( new TextComponent( (String) w ) ); + temp.add( new TextComponent( (String) w ) ); } else { - this.with.add( (BaseComponent) w ); + temp.add( (BaseComponent) w ); } } + setWith( temp ); } public void setWith(List components) @@ -47,18 +52,38 @@ public class TranslatableComponent extends BaseComponent @Override protected void toPlainText(StringBuilder builder) { - String[] parts = translate.split( "((?<=%s)|(?=%s))" ); - int i = 0; - for ( String part : parts ) + String trans = locales.getString( translate ); + if ( trans == null ) { - if ( part.equals( "%s" ) ) + builder.append( translate ); + } else + { + Matcher matcher = format.matcher( trans ); + int position = 0; + int i = 0; + while ( matcher.find( position ) ) { - with.get( i ).toPlainText( builder ); - i++; - } else - { - builder.append( part ); + int pos = matcher.start(); + if ( pos != position ) + { + builder.append( trans.substring( position, pos ) ); + } + position = matcher.end(); + + String formatCode = matcher.group( 2 ); + switch ( formatCode.charAt( 0 ) ) + { + case 's': + case 'd': + String withIndex = matcher.group( 1 ); + with.get( withIndex != null ? Integer.parseInt( withIndex ) - 1 : i++ ).toPlainText( builder ); + break; + case '%': + builder.append( '%' ); + break; + } } + } super.toPlainText( builder ); } @@ -66,28 +91,54 @@ public class TranslatableComponent extends BaseComponent @Override protected void toLegacyText(StringBuilder builder) { - String[] parts = locales.getString( translate ).split( "((?<=%s)|(?=%s))" ); - int i = 0; - for ( String part : parts ) + String trans = locales.getString( translate ); + if ( trans == null ) { - if ( part.equals( "%s" ) ) + builder.append( translate ); + } else + { + Matcher matcher = format.matcher( trans ); + int position = 0; + int i = 0; + while ( matcher.find( position ) ) { - with.get( i ).toLegacyText( builder ); - i++; - } else - { - builder.append( getColor() ); - if ( isBold() ) builder.append( ChatColor.BOLD ); - if ( isItalic() ) builder.append( ChatColor.ITALIC ); - if ( isUnderlined() ) builder.append( ChatColor.UNDERLINE ); - if ( isStrikethrough() ) builder.append( ChatColor.STRIKETHROUGH ); - if ( isObfuscated() ) builder.append( ChatColor.MAGIC ); - builder.append( part ); + int pos = matcher.start(); + if ( pos != position ) + { + addFormat( builder ); + builder.append( trans.substring( position, pos ) ); + } + position = matcher.end(); + + String formatCode = matcher.group( 2 ); + switch ( formatCode.charAt( 0 ) ) + { + case 's': + case 'd': + String withIndex = matcher.group( 1 ); + with.get( withIndex != null ? Integer.parseInt( withIndex ) - 1 : i++ ).toLegacyText( builder ); + break; + case '%': + addFormat( builder ); + builder.append( '%' ); + break; + } } + } super.toLegacyText( builder ); } + private void addFormat(StringBuilder builder) + { + builder.append( getColor() ); + if ( isBold() ) builder.append( ChatColor.BOLD ); + if ( isItalic() ) builder.append( ChatColor.ITALIC ); + if ( isUnderlined() ) builder.append( ChatColor.UNDERLINE ); + if ( isStrikethrough() ) builder.append( ChatColor.STRIKETHROUGH ); + if ( isObfuscated() ) builder.append( ChatColor.MAGIC ); + } + @Override public String toString() { From 60e2e6bfa4d3c7fe54df4ebac5d00f39b5de63cb Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Sat, 7 Dec 2013 13:35:52 +0000 Subject: [PATCH 10/18] Add /alertraw command + fix events --- .../main/java/net/md_5/bungee/BungeeCord.java | 1 + .../bungee/chat/BaseComponentSerializer.java | 8 ++-- .../md_5/bungee/command/CommandAlertRaw.java | 45 +++++++++++++++++++ 3 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 proxy/src/main/java/net/md_5/bungee/command/CommandAlertRaw.java 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 4b746c95..37a754ee 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -137,6 +137,7 @@ public class BungeeCord extends ProxyServer getPluginManager().registerCommand( null, new CommandPerms() ); getPluginManager().registerCommand( null, new CommandSend() ); getPluginManager().registerCommand( null, new CommandFind() ); + getPluginManager().registerCommand( null, new CommandAlertRaw() ); registerChannel( "BungeeCord" ); } diff --git a/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java b/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java index 65c3e6a4..f8d436ce 100644 --- a/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java +++ b/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java @@ -107,12 +107,14 @@ public class BaseComponentSerializer JsonObject clickEvent = new JsonObject(); clickEvent.addProperty( "action", component.getClickEvent().getAction().toString().toLowerCase() ); clickEvent.addProperty( "value", component.getClickEvent().getValue() ); + object.add( "clickEvent", clickEvent ); } if ( component.getHoverEvent() != null ) { - JsonObject clickEvent = new JsonObject(); - clickEvent.addProperty( "action", component.getHoverEvent().getAction().toString().toLowerCase() ); - clickEvent.add( "value", context.serialize( component.getHoverEvent().getValue() ) ); + JsonObject hoverEvent = new JsonObject(); + hoverEvent.addProperty( "action", component.getHoverEvent().getAction().toString().toLowerCase() ); + hoverEvent.add( "value", context.serialize( component.getHoverEvent().getValue() ) ); + object.add( "hoverEvent", hoverEvent ); } } } diff --git a/proxy/src/main/java/net/md_5/bungee/command/CommandAlertRaw.java b/proxy/src/main/java/net/md_5/bungee/command/CommandAlertRaw.java new file mode 100644 index 00000000..5467d73a --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/command/CommandAlertRaw.java @@ -0,0 +1,45 @@ +package net.md_5.bungee.command; + +import com.google.common.base.Joiner; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.plugin.Command; +import net.md_5.bungee.chat.ComponentSerializer; + +public class CommandAlertRaw extends Command +{ + + public CommandAlertRaw() + { + super( "alertraw", "bungeecord.command.alert" ); + } + + @Override + public void execute(CommandSender sender, String[] args) + { + if ( args.length == 0 ) + { + sender.sendMessage( ChatColor.RED + "You must supply a message." ); + } else + { + String message = Joiner.on(' ').join( args ); + + try { + ProxyServer.getInstance().broadcast( ComponentSerializer.parse( message ) ); + } catch ( Exception e ) { + TextComponent error = new TextComponent( "An error occurred while parsing your message. (Hover for details)" ); + error.setColor( ChatColor.RED ); + error.setUnderlined( true ); + + TextComponent errorMessage = new TextComponent( e.getMessage() ); + errorMessage.setColor( ChatColor.RED ); + + error.setHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, errorMessage ) ); + sender.sendMessage( error ); + } + } + } +} From 00409552042e5b583d8dd478a8923aa7f4fdd393 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Sat, 7 Dec 2013 13:59:00 +0000 Subject: [PATCH 11/18] Fix events not working when no formatting is used --- .../java/net/md_5/bungee/api/chat/BaseComponent.java | 5 +++-- .../java/net/md_5/bungee/api/chat/ClickEvent.java | 5 +++++ .../java/net/md_5/bungee/api/chat/HoverEvent.java | 5 +++++ .../md_5/bungee/chat/BaseComponentSerializer.java | 12 +++--------- .../net/md_5/bungee/command/CommandAlertRaw.java | 8 ++++++-- 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java index f004a330..c94299f5 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java @@ -277,7 +277,8 @@ public abstract class BaseComponent { return color != null || bold != null || italic != null || underlined != null || - strikethrough != null || obfuscated != null; + strikethrough != null || obfuscated != null || + hoverEvent != null || clickEvent != null; } public String toPlainText() @@ -320,6 +321,6 @@ public abstract class BaseComponent @Override public String toString() { - return String.format( "BaseComponent{color=%s, bold=%b, italic=%b, underlined=%b, strikethrough=%b, obfuscated=%b}", getColor().getName(), isBold(), isItalic(), isUnderlined(), isStrikethrough(), isObfuscated() ); + return String.format( "BaseComponent{color=%s, bold=%b, italic=%b, underlined=%b, strikethrough=%b, obfuscated=%b, clickEvent=%s, hoverEvent=%s}", getColor().getName(), isBold(), isItalic(), isUnderlined(), isStrikethrough(), isObfuscated(), getClickEvent(), getHoverEvent() ); } } diff --git a/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java b/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java index 3d0dd90b..e412b23c 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java @@ -22,4 +22,9 @@ public class ClickEvent RUN_COMMAND, SUGGEST_COMMAND } + + @Override + public String toString() { + return String.format( "ClickEvent{action=%s, value=%s}", action, value ); + } } diff --git a/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java b/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java index 8252a954..125eaede 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java @@ -42,4 +42,9 @@ public class HoverEvent SHOW_ACHIEVEMENT, SHOW_ITEM } + + @Override + public String toString() { + return String.format( "HoverEvent{action=%s, value=%s}", action, value ); + } } diff --git a/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java b/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java index f8d436ce..62689570 100644 --- a/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java +++ b/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java @@ -41,7 +41,7 @@ public class BaseComponentSerializer } if ( object.has( "extra" ) ) { - component.setExtra( Arrays.asList( (BaseComponent[]) context.deserialize( object.get( "extra" ), BaseComponent[].class ) ) ); + component.setExtra( Arrays.asList( context.deserialize( object.get( "extra" ), BaseComponent[].class ) ) ); } //Events @@ -57,14 +57,8 @@ public class BaseComponentSerializer JsonObject event = object.getAsJsonObject( "hoverEvent" ); HoverEvent hoverEvent = new HoverEvent(); hoverEvent.setAction( HoverEvent.Action.valueOf( event.get( "action" ).getAsString().toUpperCase() ) ); - Object res = context.deserialize( event.get( "value" ), BaseComponent.class ); - if ( res instanceof String ) - { - hoverEvent.setValue( (String) res ); - } else - { - hoverEvent.setValue( (BaseComponent) res ); - } + BaseComponent res = context.deserialize( event.get( "value" ), BaseComponent.class ); + hoverEvent.setValue( res ); component.setHoverEvent( hoverEvent ); } } diff --git a/proxy/src/main/java/net/md_5/bungee/command/CommandAlertRaw.java b/proxy/src/main/java/net/md_5/bungee/command/CommandAlertRaw.java index 5467d73a..6e5ab23f 100644 --- a/proxy/src/main/java/net/md_5/bungee/command/CommandAlertRaw.java +++ b/proxy/src/main/java/net/md_5/bungee/command/CommandAlertRaw.java @@ -9,6 +9,8 @@ import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.plugin.Command; import net.md_5.bungee.chat.ComponentSerializer; +import java.util.Arrays; + public class CommandAlertRaw extends Command { @@ -27,9 +29,11 @@ public class CommandAlertRaw extends Command { String message = Joiner.on(' ').join( args ); - try { + try + { ProxyServer.getInstance().broadcast( ComponentSerializer.parse( message ) ); - } catch ( Exception e ) { + } catch ( Exception e ) + { TextComponent error = new TextComponent( "An error occurred while parsing your message. (Hover for details)" ); error.setColor( ChatColor.RED ); error.setUnderlined( true ); From c17fa03ccdea92f8f34427b25e04a46983fc3a1b Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Sat, 7 Dec 2013 15:10:08 +0000 Subject: [PATCH 12/18] Remove string methods from HoverEvent. TextComponents will become strings if formatting isn't used --- .../net/md_5/bungee/api/chat/HoverEvent.java | 32 +++---------------- 1 file changed, 5 insertions(+), 27 deletions(-) diff --git a/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java b/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java index 125eaede..8f38d338 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java @@ -1,40 +1,18 @@ package net.md_5.bungee.api.chat; +import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @NoArgsConstructor +@Getter +@Setter +@AllArgsConstructor public class HoverEvent { - @Getter - @Setter private Action action; - - @Getter - private Object value; - - public HoverEvent(Action action, String value) - { - setAction( action ); - setValue( value ); - } - - public HoverEvent(Action action, BaseComponent value) - { - setAction( action ); - setValue( value ); - } - - public void setValue(String value) - { - this.value = value; - } - - public void setValue(BaseComponent value) - { - this.value = value; - } + private BaseComponent value; public enum Action { From 3f9ca85831220c5d4722b9be2ec7c3fcf261a292 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Sat, 7 Dec 2013 23:35:43 +0000 Subject: [PATCH 13/18] Add missing documentation --- .../md_5/bungee/api/chat/BaseComponent.java | 70 +++++++++++++++++-- .../net/md_5/bungee/api/chat/ClickEvent.java | 27 ++++++- .../net/md_5/bungee/api/chat/HoverEvent.java | 3 +- .../md_5/bungee/api/chat/TextComponent.java | 21 +++++- .../api/chat/TranslatableComponent.java | 51 +++++++++++++- 5 files changed, 162 insertions(+), 10 deletions(-) diff --git a/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java index c94299f5..16cae74d 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java @@ -19,28 +19,61 @@ public abstract class BaseComponent @Setter(AccessLevel.NONE) BaseComponent parent; - //Formatting + /** + * The color of this component and any child + * components (unless overridden) + */ @Getter(AccessLevel.NONE) private ChatColor color; + /** + * Whether this component and any child + * components (unless overridden) is bold + */ @Getter(AccessLevel.NONE) private Boolean bold; + /** + * Whether this component and any child + * components (unless overridden) is italic + */ @Getter(AccessLevel.NONE) private Boolean italic; + /** + * Whether this component and any child + * components (unless overridden) is underlined + */ @Getter(AccessLevel.NONE) private Boolean underlined; + /** + * Whether this component and any child + * components (unless overridden) is strikethrough + */ @Getter(AccessLevel.NONE) private Boolean strikethrough; + /** + * Whether this component and any child + * components (unless overridden) is obfuscated + */ @Getter(AccessLevel.NONE) private Boolean obfuscated; - //Appended components + /** + * Appended components that inherit this component's + * formatting and events + */ private List extra; - //Events + /** + * The action to preform when this component (and + * child components) are clicked + */ private ClickEvent clickEvent; + /** + * The action to preform when this component (and + * child components) are hovered over + */ private HoverEvent hoverEvent; - public BaseComponent(BaseComponent old) + protected BaseComponent(BaseComponent old) { setColor( old.getColorRaw() ); setBold( old.isBoldRaw() ); @@ -50,6 +83,12 @@ public abstract class BaseComponent setObfuscated( old.isObfuscatedRaw() ); } + /** + * Converts the components to a string that uses the + * old formatting codes ({@link net.md_5.bungee.api.ChatColor#COLOR_CHAR} + * @param components the components to convert + * @return the string in the old format + */ public static String toLegacyText(BaseComponent... components) { StringBuilder builder = new StringBuilder(); @@ -60,6 +99,12 @@ public abstract class BaseComponent return builder.toString(); } + /** + * Converts the components into a string without + * any formatting + * @param components the components to convert + * @return the string as plain text + */ public static String toPlainText(BaseComponent... components) { StringBuilder builder = new StringBuilder(); @@ -273,6 +318,11 @@ public abstract class BaseComponent extra.add( component ); } + /** + * Returns whether the component has any formatting + * or events applied to it + * @return + */ public boolean hasFormatting() { return color != null || bold != null || @@ -281,6 +331,12 @@ public abstract class BaseComponent hoverEvent != null || clickEvent != null; } + + /** + * Converts the component into a string without + * any formatting + * @return the string as plain text + */ public String toPlainText() { StringBuilder builder = new StringBuilder(); @@ -299,6 +355,12 @@ public abstract class BaseComponent } } + + /** + * Converts the component to a string that uses the + * old formatting codes ({@link net.md_5.bungee.api.ChatColor#COLOR_CHAR} + * @return the string in the old format + */ public String toLegacyText() { StringBuilder builder = new StringBuilder(); diff --git a/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java b/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java index e412b23c..23d83ee2 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java @@ -12,19 +12,44 @@ import lombok.Setter; @NoArgsConstructor public class ClickEvent { + /** + * The type of action to preform on click + */ private Action action; + /** + * Depends on action + * @see net.md_5.bungee.api.chat.ClickEvent.Action + */ private String value; public enum Action { + /** + * Open a url at the path given by + * {@link net.md_5.bungee.api.chat.ClickEvent#setValue(String)} + */ OPEN_URL, + /** + * Open a file at the path given by + * {@link net.md_5.bungee.api.chat.ClickEvent#setValue(String)} + */ OPEN_FILE, + /** + * Run the command given by + * {@link net.md_5.bungee.api.chat.ClickEvent#setValue(String)} + */ RUN_COMMAND, + /** + * Inserts the string given by + * {@link net.md_5.bungee.api.chat.ClickEvent#setValue(String)} + * into the players text box + */ SUGGEST_COMMAND } @Override - public String toString() { + public String toString() + { return String.format( "ClickEvent{action=%s, value=%s}", action, value ); } } diff --git a/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java b/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java index 8f38d338..a1516173 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java @@ -22,7 +22,8 @@ public class HoverEvent } @Override - public String toString() { + public String toString() + { return String.format( "HoverEvent{action=%s, value=%s}", action, value ); } } diff --git a/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java index d4e79706..839e355d 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java @@ -19,6 +19,12 @@ public class TextComponent extends BaseComponent private static final Pattern url = Pattern.compile( "^(?:(https?)://)?([-\\w_\\.]{2,}\\.[a-z]{2,4})(/\\S*)?$" ); + /** + * Converts the old formatting system that used {@link net.md_5.bungee.api.ChatColor#COLOR_CHAR} + * into the new json based system. + * @param message the text to convert + * @return the components needed to print the message to the client + */ public static BaseComponent[] fromLegacyText(String message) { ArrayList components = new ArrayList<>(); @@ -116,12 +122,21 @@ public class TextComponent extends BaseComponent return components.toArray( new BaseComponent[components.size()] ); } + /** + * The text of the component that will be + * displayed to the client + */ private String text; - public TextComponent(TextComponent old) + /** + * Creates a TextComponent with formatting and text + * from the passed component + * @param textComponent the component to copy from + */ + public TextComponent(TextComponent textComponent) { - super( old ); - setText( old.getText() ); + super( textComponent ); + setText( textComponent.getText() ); } @Override diff --git a/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java index 2730aa2a..9f96b010 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java @@ -17,12 +17,28 @@ import java.util.regex.Pattern; @NoArgsConstructor public class TranslatableComponent extends BaseComponent { - public final ResourceBundle locales = ResourceBundle.getBundle( "mojang-translations/en_US" ); + private final ResourceBundle locales = ResourceBundle.getBundle( "mojang-translations/en_US" ); private final Pattern format = Pattern.compile( "%(?:(\\d+)\\$)?([A-Za-z%]|$)" ); + /** + * The key into the Minecraft locale files to use for the + * translation. The text depends on the client's locale setting. + * The console is always en_US + */ private String translate; + /** + * The components to substitute into the translation + */ private List with; + /** + * Creates a translatable component with the passed substitutions + * @see #setTranslate(String) + * @see #setWith(java.util.List) + * @param translate the translation key + * @param with the {@link java.lang.String}s and {@link net.md_5.bungee.api.chat.BaseComponent}s + * to use into the translation + */ public TranslatableComponent(String translate, Object... with) { setTranslate( translate ); @@ -40,6 +56,12 @@ public class TranslatableComponent extends BaseComponent setWith( temp ); } + /** + * Sets the translation substitutions to be used in + * this component. Removes any previously set + * substitutions + * @param components the components to substitute + */ public void setWith(List components) { for ( BaseComponent component : components ) @@ -49,6 +71,33 @@ public class TranslatableComponent extends BaseComponent with = components; } + /** + * Adds a text substitution to the component. The text will + * inherit this component's formatting + * + * @param text the text to substitute + */ + public void addWith(String text) + { + addWith( new TextComponent( text ) ); + } + + /** + * Adds a component substitution to the component. The text will + * inherit this component's formatting + * + * @param component the component to substitute + */ + public void addWith(BaseComponent component) + { + if ( with == null ) + { + with = new ArrayList<>(); + } + component.parent = this; + with.add( component ); + } + @Override protected void toPlainText(StringBuilder builder) { From 4ef15ae7642dc53945675e1f68e47db930f8144d Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Sun, 8 Dec 2013 00:30:21 +0000 Subject: [PATCH 14/18] Fixed TranslatableComponents missing the end of the translated text --- .../md_5/bungee/api/chat/TranslatableComponent.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java index 9f96b010..f24d252d 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java @@ -132,7 +132,10 @@ public class TranslatableComponent extends BaseComponent break; } } - + if ( trans.length() != position ) + { + builder.append( trans.substring( position, trans.length() ) ); + } } super.toPlainText( builder ); } @@ -143,6 +146,7 @@ public class TranslatableComponent extends BaseComponent String trans = locales.getString( translate ); if ( trans == null ) { + addFormat( builder ); builder.append( translate ); } else { @@ -173,7 +177,11 @@ public class TranslatableComponent extends BaseComponent break; } } - + if ( trans.length() != position ) + { + addFormat( builder ); + builder.append( trans.substring( position, trans.length() ) ); + } } super.toLegacyText( builder ); } From 70bb3ddcceda76fb5c177af26bf3d027af62f3f7 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Sun, 8 Dec 2013 00:48:10 +0000 Subject: [PATCH 15/18] Added some unit tests --- .../net/md_5/bungee/chat/ComponentsTest.java | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 proxy/src/test/java/net/md_5/bungee/chat/ComponentsTest.java diff --git a/proxy/src/test/java/net/md_5/bungee/chat/ComponentsTest.java b/proxy/src/test/java/net/md_5/bungee/chat/ComponentsTest.java new file mode 100644 index 00000000..e6d7ec39 --- /dev/null +++ b/proxy/src/test/java/net/md_5/bungee/chat/ComponentsTest.java @@ -0,0 +1,60 @@ +package net.md_5.bungee.chat; + +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.chat.TranslatableComponent; +import org.junit.Assert; +import org.junit.Test; + +public class ComponentsTest +{ + @Test + public void testBasicComponent() { + TextComponent textComponent = new TextComponent( "Hello world" ); + textComponent.setColor( ChatColor.RED ); + + Assert.assertEquals( "Hello world" , textComponent.toPlainText() ); + Assert.assertEquals( ChatColor.RED + "Hello world" , textComponent.toLegacyText() ); + } + + @Test + public void testLegacyConverter() { + BaseComponent[] test1 = TextComponent.fromLegacyText( ChatColor.AQUA + "Aqua " + ChatColor.RED + ChatColor.BOLD + "RedBold" ); + + Assert.assertEquals( "Aqua RedBold" , BaseComponent.toPlainText( test1 ) ); + Assert.assertEquals( ChatColor.AQUA + "Aqua " + ChatColor.RED + ChatColor.BOLD + "RedBold" , BaseComponent.toLegacyText( test1 ) ); + + BaseComponent[] test2 = TextComponent.fromLegacyText( "Text http://spigotmc.org " + ChatColor.GREEN + "google.com/test" ); + + Assert.assertEquals( "Text http://spigotmc.org google.com/test" , BaseComponent.toPlainText( test2 ) ); + //The extra ChatColor.WHITEs are sometimes inserted when not needed but it doesn't change the result + Assert.assertEquals( ChatColor.WHITE + "Text " + ChatColor.WHITE + "http://spigotmc.org" + ChatColor.WHITE + + " " + ChatColor.GREEN + "google.com/test" , BaseComponent.toLegacyText( test2 ) ); + + ClickEvent url1 = test2[1].getClickEvent(); + Assert.assertNotNull( url1 ); + Assert.assertTrue( url1.getAction() == ClickEvent.Action.OPEN_URL ); + Assert.assertEquals( "http://spigotmc.org" , url1.getValue() ); + + ClickEvent url2 = test2[3].getClickEvent(); + Assert.assertNotNull( url2 ); + Assert.assertTrue( url2.getAction() == ClickEvent.Action.OPEN_URL ); + Assert.assertEquals( "http://google.com/test" , url2.getValue() ); + } + + @Test + public void testTranslateComponent() { + TranslatableComponent item = new TranslatableComponent( "item.swordGold.name" ); + item.setColor( ChatColor.AQUA ); + TranslatableComponent translatableComponent = new TranslatableComponent( "commands.give.success", + item, "5", + "thinkofdeath"); + + Assert.assertEquals( "Given Golden Sword * 5 to thinkofdeath" , translatableComponent.toPlainText() ); + Assert.assertEquals( ChatColor.WHITE + "Given " + ChatColor.AQUA + "Golden Sword" + ChatColor.WHITE + + " * " + ChatColor.WHITE + "5" + ChatColor.WHITE + " to " + ChatColor.WHITE + "thinkofdeath" , + translatableComponent.toLegacyText() ); + } +} From bc746a546fb848e24412244700984222effa4ade Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Sun, 8 Dec 2013 10:17:02 +0000 Subject: [PATCH 16/18] Added another unit test + formatted code --- .../net/md_5/bungee/chat/ComponentsTest.java | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/proxy/src/test/java/net/md_5/bungee/chat/ComponentsTest.java b/proxy/src/test/java/net/md_5/bungee/chat/ComponentsTest.java index e6d7ec39..584310d1 100644 --- a/proxy/src/test/java/net/md_5/bungee/chat/ComponentsTest.java +++ b/proxy/src/test/java/net/md_5/bungee/chat/ComponentsTest.java @@ -11,50 +11,59 @@ import org.junit.Test; public class ComponentsTest { @Test - public void testBasicComponent() { + public void testBasicComponent() + { TextComponent textComponent = new TextComponent( "Hello world" ); textComponent.setColor( ChatColor.RED ); - Assert.assertEquals( "Hello world" , textComponent.toPlainText() ); - Assert.assertEquals( ChatColor.RED + "Hello world" , textComponent.toLegacyText() ); + Assert.assertEquals( "Hello world", textComponent.toPlainText() ); + Assert.assertEquals( ChatColor.RED + "Hello world", textComponent.toLegacyText() ); } @Test - public void testLegacyConverter() { + public void testLegacyConverter() + { BaseComponent[] test1 = TextComponent.fromLegacyText( ChatColor.AQUA + "Aqua " + ChatColor.RED + ChatColor.BOLD + "RedBold" ); - Assert.assertEquals( "Aqua RedBold" , BaseComponent.toPlainText( test1 ) ); - Assert.assertEquals( ChatColor.AQUA + "Aqua " + ChatColor.RED + ChatColor.BOLD + "RedBold" , BaseComponent.toLegacyText( test1 ) ); + Assert.assertEquals( "Aqua RedBold", BaseComponent.toPlainText( test1 ) ); + Assert.assertEquals( ChatColor.AQUA + "Aqua " + ChatColor.RED + ChatColor.BOLD + "RedBold", BaseComponent.toLegacyText( test1 ) ); BaseComponent[] test2 = TextComponent.fromLegacyText( "Text http://spigotmc.org " + ChatColor.GREEN + "google.com/test" ); - Assert.assertEquals( "Text http://spigotmc.org google.com/test" , BaseComponent.toPlainText( test2 ) ); + Assert.assertEquals( "Text http://spigotmc.org google.com/test", BaseComponent.toPlainText( test2 ) ); //The extra ChatColor.WHITEs are sometimes inserted when not needed but it doesn't change the result Assert.assertEquals( ChatColor.WHITE + "Text " + ChatColor.WHITE + "http://spigotmc.org" + ChatColor.WHITE - + " " + ChatColor.GREEN + "google.com/test" , BaseComponent.toLegacyText( test2 ) ); + + " " + ChatColor.GREEN + "google.com/test", BaseComponent.toLegacyText( test2 ) ); ClickEvent url1 = test2[1].getClickEvent(); Assert.assertNotNull( url1 ); Assert.assertTrue( url1.getAction() == ClickEvent.Action.OPEN_URL ); - Assert.assertEquals( "http://spigotmc.org" , url1.getValue() ); + Assert.assertEquals( "http://spigotmc.org", url1.getValue() ); ClickEvent url2 = test2[3].getClickEvent(); Assert.assertNotNull( url2 ); Assert.assertTrue( url2.getAction() == ClickEvent.Action.OPEN_URL ); - Assert.assertEquals( "http://google.com/test" , url2.getValue() ); + Assert.assertEquals( "http://google.com/test", url2.getValue() ); } @Test - public void testTranslateComponent() { + public void testTranslateComponent() + { TranslatableComponent item = new TranslatableComponent( "item.swordGold.name" ); item.setColor( ChatColor.AQUA ); TranslatableComponent translatableComponent = new TranslatableComponent( "commands.give.success", item, "5", - "thinkofdeath"); + "thinkofdeath" ); - Assert.assertEquals( "Given Golden Sword * 5 to thinkofdeath" , translatableComponent.toPlainText() ); + Assert.assertEquals( "Given Golden Sword * 5 to thinkofdeath", translatableComponent.toPlainText() ); Assert.assertEquals( ChatColor.WHITE + "Given " + ChatColor.AQUA + "Golden Sword" + ChatColor.WHITE - + " * " + ChatColor.WHITE + "5" + ChatColor.WHITE + " to " + ChatColor.WHITE + "thinkofdeath" , + + " * " + ChatColor.WHITE + "5" + ChatColor.WHITE + " to " + ChatColor.WHITE + "thinkofdeath", translatableComponent.toLegacyText() ); + + TranslatableComponent positional = new TranslatableComponent( "book.pageIndicator", "5", "50" ); + + Assert.assertEquals( "Page 5 of 50", positional.toPlainText() ); + Assert.assertEquals( ChatColor.WHITE + "Page " + ChatColor.WHITE + "5" + ChatColor.WHITE + " of " + ChatColor.WHITE + "50", positional.toLegacyText() ); } + } From 5d68b422e5d59c9f6d18b258a4b798e2e3a71d6b Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Tue, 10 Dec 2013 11:50:34 +0000 Subject: [PATCH 17/18] Add ComponentBuilder + make events final An example of ComponentBuilder usage can be found at CommandAlertRaw --- .../md_5/bungee/api/chat/BaseComponent.java | 2 + .../net/md_5/bungee/api/chat/ClickEvent.java | 16 +- .../bungee/api/chat/ComponentBuilder.java | 157 ++++++++++++++++++ .../net/md_5/bungee/api/chat/HoverEvent.java | 8 +- .../md_5/bungee/api/chat/TextComponent.java | 15 +- .../bungee/chat/BaseComponentSerializer.java | 12 +- .../md_5/bungee/command/CommandAlertRaw.java | 16 +- .../net/md_5/bungee/chat/ComponentsTest.java | 13 ++ 8 files changed, 199 insertions(+), 40 deletions(-) create mode 100644 api/src/main/java/net/md_5/bungee/api/chat/ComponentBuilder.java diff --git a/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java index 16cae74d..6607d92a 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java @@ -81,6 +81,8 @@ public abstract class BaseComponent setUnderlined( old.isUnderlined() ); setStrikethrough( old.isStrikethroughRaw() ); setObfuscated( old.isObfuscatedRaw() ); + setClickEvent( old.getClickEvent() ); + setHoverEvent( old.getHoverEvent() ); } /** diff --git a/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java b/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java index 23d83ee2..55c66f30 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java @@ -7,41 +7,39 @@ import lombok.NoArgsConstructor; import lombok.Setter; @Getter -@Setter @AllArgsConstructor -@NoArgsConstructor -public class ClickEvent +final public class ClickEvent { /** * The type of action to preform on click */ - private Action action; + private final Action action; /** * Depends on action * @see net.md_5.bungee.api.chat.ClickEvent.Action */ - private String value; + private final String value; public enum Action { /** * Open a url at the path given by - * {@link net.md_5.bungee.api.chat.ClickEvent#setValue(String)} + * {@link net.md_5.bungee.api.chat.ClickEvent#getValue()} */ OPEN_URL, /** * Open a file at the path given by - * {@link net.md_5.bungee.api.chat.ClickEvent#setValue(String)} + * {@link net.md_5.bungee.api.chat.ClickEvent#getValue()} */ OPEN_FILE, /** * Run the command given by - * {@link net.md_5.bungee.api.chat.ClickEvent#setValue(String)} + * {@link net.md_5.bungee.api.chat.ClickEvent#getValue()} */ RUN_COMMAND, /** * Inserts the string given by - * {@link net.md_5.bungee.api.chat.ClickEvent#setValue(String)} + * {@link net.md_5.bungee.api.chat.ClickEvent#getValue()} * into the players text box */ SUGGEST_COMMAND diff --git a/api/src/main/java/net/md_5/bungee/api/chat/ComponentBuilder.java b/api/src/main/java/net/md_5/bungee/api/chat/ComponentBuilder.java new file mode 100644 index 00000000..038ae5c7 --- /dev/null +++ b/api/src/main/java/net/md_5/bungee/api/chat/ComponentBuilder.java @@ -0,0 +1,157 @@ +package net.md_5.bungee.api.chat; + +import lombok.NoArgsConstructor; +import net.md_5.bungee.api.ChatColor; + +import java.util.ArrayList; +import java.util.List; + +/** + * ComponentBuilder simplifies creating basic messages by allowing + * the use of a chainable builder. + *

+ *

+ *     new ComponentBuilder("Hello ").color(ChatColor.RED).
+ *         append("World").color(ChatColor.BLUE).
+ *         append("!").bold(true).create();
+ * 
+ *

+ * All methods (excluding {@link #append(String)} and {@link #create()} + * work on the last part appended to the builder, so in the example + * above "Hello " would be {@link net.md_5.bungee.api.ChatColor#RED} + * and "World" would be {@link net.md_5.bungee.api.ChatColor#BLUE} but + * "!" would be bold and {@link net.md_5.bungee.api.ChatColor#BLUE} + * because append copies the previous part's formatting + */ +public class ComponentBuilder +{ + private TextComponent current; + private List parts = new ArrayList<>(); + + /** + * Creates a componentBuilder with the given text as the + * first part. + * + * @param text the first text element + */ + public ComponentBuilder(String text) + { + current = new TextComponent( text ); + } + + /** + * Appends the text to the builder and makes it the current + * target for formatting. The text will have all the + * formatting from the previous part. + * + * @param text the text to append + * @return this ComponentBuilder for chaining + */ + public ComponentBuilder append(String text) + { + parts.add( current ); + current = new TextComponent( current ); + current.setText( text ); + return this; + } + + /** + * Sets the color of the current part. + * + * @param color the new color + * @return this ComponentBuilder for chaining + */ + public ComponentBuilder color(ChatColor color) + { + current.setColor( color ); + return this; + } + + /** + * Sets whether the current part is bold. + * + * @param bold whether this part is bold + * @return this ComponentBuilder for chaining + */ + public ComponentBuilder bold(boolean bold) + { + current.setBold( bold ); + return this; + } + + /** + * Sets whether the current part is italic + * + * @param italic whether this part is italic + * @return this ComponentBuilder for chaining + */ + public ComponentBuilder italic(boolean italic) + { + current.setItalic( italic ); + return this; + } + + /** + * Sets whether the current part is underlined + * + * @param underlined whether this part is underlined + * @return this ComponentBuilder for chaining + */ + public ComponentBuilder underlined(boolean underlined) + { + current.setUnderlined( underlined ); + return this; + } + + /** + * Sets whether the current part is strikethrough + * + * @param strikethrough whether this part is strikethrough + * @return this ComponentBuilder for chaining + */ + public ComponentBuilder strikethrough(boolean strikethrough) + { + current.setStrikethrough( strikethrough ); + return this; + } + + /** + * Sets whether the current pat is obfuscated + * + * @param obfuscated whether this part is obfuscated + * @return this ComponentBuilder for chaining + */ + public ComponentBuilder obfuscated(boolean obfuscated) + { + current.setObfuscated( obfuscated ); + return this; + } + + /** + * Sets the click event for the current part. + * @param clickEvent + * @return + */ + public ComponentBuilder event(ClickEvent clickEvent) + { + current.setClickEvent( clickEvent ); + return this; + } + + public ComponentBuilder event(HoverEvent hoverEvent) + { + current.setHoverEvent( hoverEvent ); + return this; + } + + /** + * Returns the components needed to display the message + * created by this builder + * @return the created components + */ + public BaseComponent[] create() + { + parts.add( current ); + return parts.toArray( new BaseComponent[parts.size()] ); + } +} diff --git a/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java b/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java index a1516173..c2df8f9c 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java @@ -5,14 +5,12 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -@NoArgsConstructor @Getter -@Setter @AllArgsConstructor -public class HoverEvent +final public class HoverEvent { - private Action action; - private BaseComponent value; + private final Action action; + private final BaseComponent[] value; public enum Action { diff --git a/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java index 839e355d..e1c68c07 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java @@ -94,19 +94,10 @@ public class TextComponent extends BaseComponent TextComponent old = component; component = new TextComponent( old ); - ClickEvent clickEvent = new ClickEvent(); - clickEvent.setAction( ClickEvent.Action.OPEN_URL ); String urlString = message.substring( i, pos ); - if ( urlString.startsWith( "http" ) ) - { - component.setText( urlString ); - clickEvent.setValue( urlString ); - } else - { - component.setText( urlString ); - clickEvent.setValue( "http://" + urlString ); - } - component.setClickEvent( clickEvent ); + component.setText( urlString ); + component.setClickEvent( new ClickEvent( ClickEvent.Action.OPEN_URL, + urlString.startsWith( "http" ) ? urlString : "http://" + urlString ) ); components.add( component ); i += pos - i - 1; component = old; diff --git a/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java b/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java index 62689570..7128dab9 100644 --- a/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java +++ b/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java @@ -55,11 +55,13 @@ public class BaseComponentSerializer if ( object.has( "hoverEvent" ) ) { JsonObject event = object.getAsJsonObject( "hoverEvent" ); - HoverEvent hoverEvent = new HoverEvent(); - hoverEvent.setAction( HoverEvent.Action.valueOf( event.get( "action" ).getAsString().toUpperCase() ) ); - BaseComponent res = context.deserialize( event.get( "value" ), BaseComponent.class ); - hoverEvent.setValue( res ); - component.setHoverEvent( hoverEvent ); + BaseComponent[] res; + if (event.get("value").isJsonArray()) { + res = context.deserialize( event.get( "value" ), BaseComponent[].class ); + } else { + res = new BaseComponent[]{context.deserialize( event.get( "value" ), BaseComponent.class )}; + } + component.setHoverEvent( new HoverEvent( HoverEvent.Action.valueOf( event.get( "action" ).getAsString().toUpperCase() ), res ) ); } } diff --git a/proxy/src/main/java/net/md_5/bungee/command/CommandAlertRaw.java b/proxy/src/main/java/net/md_5/bungee/command/CommandAlertRaw.java index 6e5ab23f..39f12ba1 100644 --- a/proxy/src/main/java/net/md_5/bungee/command/CommandAlertRaw.java +++ b/proxy/src/main/java/net/md_5/bungee/command/CommandAlertRaw.java @@ -4,6 +4,7 @@ import com.google.common.base.Joiner; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.chat.ComponentBuilder; import net.md_5.bungee.api.chat.HoverEvent; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.plugin.Command; @@ -34,15 +35,12 @@ public class CommandAlertRaw extends Command ProxyServer.getInstance().broadcast( ComponentSerializer.parse( message ) ); } catch ( Exception e ) { - TextComponent error = new TextComponent( "An error occurred while parsing your message. (Hover for details)" ); - error.setColor( ChatColor.RED ); - error.setUnderlined( true ); - - TextComponent errorMessage = new TextComponent( e.getMessage() ); - errorMessage.setColor( ChatColor.RED ); - - error.setHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, errorMessage ) ); - sender.sendMessage( error ); + sender.sendMessage( + new ComponentBuilder( "An error occured while parsing your message. (Hover for details)" ). + color( ChatColor.RED ).underlined( true ). + event( new HoverEvent( HoverEvent.Action.SHOW_TEXT, + new ComponentBuilder( e.getMessage() ).color( ChatColor.RED ).create() ) ). + create() ); } } } diff --git a/proxy/src/test/java/net/md_5/bungee/chat/ComponentsTest.java b/proxy/src/test/java/net/md_5/bungee/chat/ComponentsTest.java index 584310d1..898c2b5f 100644 --- a/proxy/src/test/java/net/md_5/bungee/chat/ComponentsTest.java +++ b/proxy/src/test/java/net/md_5/bungee/chat/ComponentsTest.java @@ -3,6 +3,7 @@ package net.md_5.bungee.chat; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.ComponentBuilder; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.TranslatableComponent; import org.junit.Assert; @@ -66,4 +67,16 @@ public class ComponentsTest Assert.assertEquals( ChatColor.WHITE + "Page " + ChatColor.WHITE + "5" + ChatColor.WHITE + " of " + ChatColor.WHITE + "50", positional.toLegacyText() ); } + @Test + public void testBuilder() + { + BaseComponent[] components = new ComponentBuilder( "Hello " ).color( ChatColor.RED ). + append( "World" ).bold( true ).color( ChatColor.BLUE ). + append( "!" ).color( ChatColor.YELLOW ).create(); + + Assert.assertEquals( "Hello World!", BaseComponent.toPlainText( components ) ); + Assert.assertEquals( ChatColor.RED + "Hello " + ChatColor.BLUE + ChatColor.BOLD + + "World" + ChatColor.YELLOW + ChatColor.BOLD + "!", BaseComponent.toLegacyText( components ) ); + } + } From 00ac965d42d555746cfebc54e86d294a55b7fd3c Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Sat, 14 Dec 2013 10:16:58 +0000 Subject: [PATCH 18/18] Remove class Getter annotation --- api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java index 6607d92a..9ef3cc30 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java @@ -9,7 +9,6 @@ import net.md_5.bungee.api.ChatColor; import java.util.ArrayList; import java.util.List; -@Getter @Setter @NoArgsConstructor public abstract class BaseComponent @@ -60,17 +59,20 @@ public abstract class BaseComponent * Appended components that inherit this component's * formatting and events */ + @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; protected BaseComponent(BaseComponent old)