Skip to content

Instantly share code, notes, and snippets.

@BananaPuncher714
Last active April 4, 2021 17:12
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BananaPuncher714/ebe5ac0cec83cd8cf0b1381ef2cf36de to your computer and use it in GitHub Desktop.
Save BananaPuncher714/ebe5ac0cec83cd8cf0b1381ef2cf36de to your computer and use it in GitHub Desktop.
A simple utility class to convert legacy or B+ formatted strings into TextComponents
package your.package.here;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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.HoverEvent;
import net.md_5.bungee.api.chat.TextComponent;
/**
* A fast and easy way to convert legacy or B+ formatted chat into MD's ChatComponent API
*
* Ported over from https://github.com/BananaPuncher714/BrickBoard/tree/master/io/github/bananapuncher714/brickboard/api/chat
*
* Find out more about B+ formatting here: https://bananapuncher714.gitbook.io/brickboard/b+-style-chat-formatting
*
* @author BananaPuncher714
*/
public class MDChat {
private final static char PLACEHOLDER = '\u02A7';
/**
* Gets a TextComponent from a message
*
* @param message
* Must be legacy formatted
* @return
* The newly created TextComponent
*/
public static TextComponent getMessageFromString( String message ) {
return getMessageFromString( message, false );
}
/**
* Gets a TextComponent from a message
*
* @param message
* A legacy or B+ formatted message
* @param readExtra
* Whether or not to parse B+ formatting, A.K.A. hover and click events
* @return
* Newly created TextComponent
*/
public static TextComponent getMessageFromString( String message, boolean readExtra ) {
TextComponent chatMessage = new TextComponent( "" );
if ( message == null ) {
return chatMessage;
}
BaseComponent last = new TextComponent( "" );
List< TextComponent > actions = new ArrayList< TextComponent >();
// This is where we start looking for B+ formatting
if ( readExtra ) {
// Thanks to StarShadow#3546 for negative look behind
// Will stop certain strings from getting converted
List< String > caught = getMatches( message, "<(.+?)(?<!\\\\)>" );
for ( String action : caught ) {
String hover = getMatch( action, "\\{(.+?)(?<!\\\\)\\}" );
String click = getMatch( action, "\\((.+?\\:.+?)(?<!\\\\)\\)" );
if ( hover == null && click == null ) {
// Requires a click or hover, or skip
continue;
}
String desc = action;
HoverEvent hAction = null;
ClickEvent cAction = null;
if ( click != null ) {
String[] clickSplit = click.split( "\\:", 2 );
ClickEvent.Action cActionAction;
try {
cActionAction = ClickEvent.Action.valueOf( clickSplit[ 0 ].toUpperCase() );
} catch ( IllegalArgumentException exception ) {
cActionAction = null;
}
if ( cActionAction == null ) {
if ( hover == null ) {
continue;
}
} else {
cAction = new ClickEvent( cActionAction, clickSplit[ 1 ] );
desc = desc.replace( "(" + click + ")", "" );
}
}
if ( hover != null ) {
hAction = new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( hover.replace( "\\n", "\n" ) ).create() );
desc = desc.replace( "{" + hover + "}", "" );
}
// Replace all the escaped tags
desc = desc.replace( "\\{", "{" ).replace( "\\}", "}" );
desc = desc.replace( "\\(", "(" ).replace( "\\)", ")" );
TextComponent description = getMessageFromString( desc, false );
for ( BaseComponent component : description.getExtra() ) {
component.setHoverEvent( hAction );
component.setClickEvent( cAction );
}
message = message.replace( "<" + action + ">", "" + ChatColor.COLOR_CHAR + PLACEHOLDER );
actions.add( description );
}
// Replace all the escaped tags
message = message.replace( "\\<", "<" ).replace( "\\>", ">" );
}
// More efficient conversion using split, as opposed to iterating over every character
String[] parts = ( " " + message ).split( "" + ChatColor.COLOR_CHAR );
for ( String part : parts ) {
if ( part.isEmpty() ) {
continue;
}
char colorCharacter = part.charAt( 0 );
if ( colorCharacter == PLACEHOLDER ) {
chatMessage.addExtra( actions.remove( 0 ) );
} else {
ChatColor color = ChatColor.getByChar( colorCharacter );
if ( color != null ) {
if ( isColor( color ) ) {
clearFormatting( last );
last.setColor( color );
} else {
if ( color == ChatColor.BOLD ) {
last.setBold( true );
} else if ( color == ChatColor.ITALIC ) {
last.setItalic( true );
} else if ( color == ChatColor.UNDERLINE ) {
last.setUnderlined( true );
} else if ( color == ChatColor.MAGIC ) {
last.setObfuscated( true );
} else if ( color == ChatColor.RESET ) {
clearFormatting( last );
} else if ( color == ChatColor.STRIKETHROUGH ) {
last.setStrikethrough( true );
}
}
}
}
if ( part.length() > 1 ) {
if ( last instanceof TextComponent ) {
( ( TextComponent ) last ).setText( part.substring( 1 ) );
}
chatMessage.addExtra( last );
last = last.duplicate();
}
}
return chatMessage;
}
// Simple method to clear all formats, something that's pretty useful but not implemented *cough*
private static void clearFormatting( BaseComponent component ) {
component.setBold( false );
component.setItalic( false );
component.setUnderlined( false );
component.setObfuscated( false );
component.setStrikethrough( false );
}
// Funnily enough, MD's ChatColor doesn't have this method
private static boolean isColor( ChatColor color ) {
return org.bukkit.ChatColor.valueOf( color.name() ).isColor();
}
private static List< String > getMatches( String string, String regex ) {
Pattern pattern = Pattern.compile( regex );
Matcher matcher = pattern.matcher( string );
List< String > matches = new ArrayList< String >();
while ( matcher.find() ) {
matches.add( matcher.group( 1 ) );
}
return matches;
}
private static String getMatch( String string, String regex ) {
Pattern pattern = Pattern.compile( regex );
Matcher matcher = pattern.matcher( string );
if ( matcher.find() ) {
return matcher.group( 1 );
} else {
return null;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment