diff --git a/api/pom.xml b/api/pom.xml index 0c45e316..15eca362 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -20,9 +20,9 @@ - com.google.code.gson - gson - 2.2.4 + net.md-5 + bungeecord-chat + ${project.version} compile diff --git a/chat/nb-configuration.xml b/chat/nb-configuration.xml new file mode 100644 index 00000000..7e465924 --- /dev/null +++ b/chat/nb-configuration.xml @@ -0,0 +1,31 @@ + + + + + + project + NEW_LINE + NEW_LINE + NEW_LINE + true + true + true + true + true + true + true + true + true + true + + diff --git a/chat/pom.xml b/chat/pom.xml new file mode 100644 index 00000000..f3a76a1f --- /dev/null +++ b/chat/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + + net.md-5 + bungeecord-parent + 1.7-SNAPSHOT + ../pom.xml + + + net.md-5 + bungeecord-chat + 1.7-SNAPSHOT + jar + + BungeeCord-Chat + Minecraft JSON chat API intended for use with BungeeCord + + + + com.google.code.gson + gson + 2.2.4 + compile + + + diff --git a/api/src/main/java/net/md_5/bungee/api/ChatColor.java b/chat/src/main/java/net/md_5/bungee/api/ChatColor.java similarity index 100% rename from api/src/main/java/net/md_5/bungee/api/ChatColor.java rename to chat/src/main/java/net/md_5/bungee/api/ChatColor.java diff --git a/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java b/chat/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java similarity index 100% rename from api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java rename to chat/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java diff --git a/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java b/chat/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java similarity index 100% rename from api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java rename to chat/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java diff --git a/api/src/main/java/net/md_5/bungee/api/chat/ComponentBuilder.java b/chat/src/main/java/net/md_5/bungee/api/chat/ComponentBuilder.java similarity index 100% rename from api/src/main/java/net/md_5/bungee/api/chat/ComponentBuilder.java rename to chat/src/main/java/net/md_5/bungee/api/chat/ComponentBuilder.java diff --git a/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java b/chat/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java similarity index 100% rename from api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java rename to chat/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java diff --git a/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java b/chat/src/main/java/net/md_5/bungee/api/chat/TextComponent.java similarity index 100% rename from api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java rename to chat/src/main/java/net/md_5/bungee/api/chat/TextComponent.java diff --git a/api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java b/chat/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java similarity index 100% rename from api/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java rename to chat/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java diff --git a/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java new file mode 100644 index 00000000..bf17391f --- /dev/null +++ b/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java @@ -0,0 +1,141 @@ +package net.md_5.bungee.chat; + +import com.google.common.base.Preconditions; +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; +import java.util.HashSet; + +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( 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" ); + 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 ) ); + } + } + + protected void serialize(JsonObject object, BaseComponent component, JsonSerializationContext context) + { + boolean first = false; + if ( ComponentSerializer.serializedComponents.get() == null ) + { + first = true; + ComponentSerializer.serializedComponents.set( new HashSet() ); + } + try + { + Preconditions.checkArgument( !ComponentSerializer.serializedComponents.get().contains( component ), "Component loop" ); + ComponentSerializer.serializedComponents.get().add( component ); + 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() ); + object.add( "clickEvent", clickEvent ); + } + if ( component.getHoverEvent() != null ) + { + JsonObject hoverEvent = new JsonObject(); + hoverEvent.addProperty( "action", component.getHoverEvent().getAction().toString().toLowerCase() ); + hoverEvent.add( "value", context.serialize( component.getHoverEvent().getValue() ) ); + object.add( "hoverEvent", hoverEvent ); + } + } finally + { + ComponentSerializer.serializedComponents.get().remove( component ); + if ( first ) + { + ComponentSerializer.serializedComponents.set( null ); + } + } + } +} diff --git a/chat/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java new file mode 100644 index 00000000..b001ef85 --- /dev/null +++ b/chat/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java @@ -0,0 +1,64 @@ +package net.md_5.bungee.chat; + +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 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.HashSet; + +public class ComponentSerializer implements JsonDeserializer +{ + + private final static Gson gson = new GsonBuilder(). + registerTypeAdapter( BaseComponent.class, new ComponentSerializer() ). + registerTypeAdapter( TextComponent.class, new TextComponentSerializer() ). + registerTypeAdapter( TranslatableComponent.class, new TranslatableComponentSerializer() ). + create(); + + public final static ThreadLocal> serializedComponents = new ThreadLocal<>(); + + 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( new TextComponent( components ) ); + } + + @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 ); + } +} diff --git a/chat/src/main/java/net/md_5/bungee/chat/TextComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/TextComponentSerializer.java new file mode 100644 index 00000000..37c6e86d --- /dev/null +++ b/chat/src/main/java/net/md_5/bungee/chat/TextComponentSerializer.java @@ -0,0 +1,43 @@ +package net.md_5.bungee.chat; + +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.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; + +import java.lang.reflect.Type; +import java.util.List; + +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) + { + List extra = src.getExtra(); + if ( !src.hasFormatting() && ( extra == null || extra.isEmpty() ) ) + { + return new JsonPrimitive( src.getText() ); + } + JsonObject object = new JsonObject(); + serialize( object, src, context ); + object.addProperty( "text", src.getText() ); + return object; + } +} diff --git a/chat/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java new file mode 100644 index 00000000..c01af268 --- /dev/null +++ b/chat/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java @@ -0,0 +1,45 @@ +package net.md_5.bungee.chat; + +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.TranslatableComponent; + +import java.lang.reflect.Type; +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/pom.xml b/pom.xml index 2f6acd73..7dd7351f 100644 --- a/pom.xml +++ b/pom.xml @@ -39,6 +39,7 @@ api bootstrap + chat config event module