#2342: Add score and selector components to chat API

This commit is contained in:
Senmori
2018-02-19 11:47:21 +11:00
committed by md_5
parent e23195f5f2
commit a3b44aa612
11 changed files with 420 additions and 7 deletions

View File

@@ -55,6 +55,9 @@ import net.md_5.bungee.api.ReconnectHandler;
import net.md_5.bungee.api.ServerPing;
import net.md_5.bungee.api.Title;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.KeybindComponent;
import net.md_5.bungee.api.chat.ScoreComponent;
import net.md_5.bungee.api.chat.SelectorComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.chat.TranslatableComponent;
import net.md_5.bungee.api.config.ConfigurationAdapter;
@@ -64,6 +67,9 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.api.plugin.PluginManager;
import net.md_5.bungee.chat.ComponentSerializer;
import net.md_5.bungee.chat.KeybindComponentSerializer;
import net.md_5.bungee.chat.ScoreComponentSerializer;
import net.md_5.bungee.chat.SelectorComponentSerializer;
import net.md_5.bungee.chat.TextComponentSerializer;
import net.md_5.bungee.chat.TranslatableComponentSerializer;
import net.md_5.bungee.command.CommandBungee;
@@ -152,6 +158,9 @@ public class BungeeCord extends ProxyServer
.registerTypeAdapter( BaseComponent.class, new ComponentSerializer() )
.registerTypeAdapter( TextComponent.class, new TextComponentSerializer() )
.registerTypeAdapter( TranslatableComponent.class, new TranslatableComponentSerializer() )
.registerTypeAdapter( KeybindComponent.class, new KeybindComponentSerializer() )
.registerTypeAdapter( ScoreComponent.class, new ScoreComponentSerializer() )
.registerTypeAdapter( SelectorComponent.class, new SelectorComponentSerializer() )
.registerTypeAdapter( ServerPing.PlayerInfo.class, new PlayerInfoSerializer() )
.registerTypeAdapter( Favicon.class, Favicon.getFaviconTypeAdapter() ).create();
@Getter

View File

@@ -61,6 +61,7 @@ import net.md_5.bungee.protocol.packet.SetCompression;
import net.md_5.bungee.tab.ServerUnique;
import net.md_5.bungee.tab.TabList;
import net.md_5.bungee.util.CaseInsensitiveSet;
import net.md_5.bungee.util.ChatComponentTransformer;
@RequiredArgsConstructor
public final class UserConnection implements ProxiedPlayer
@@ -404,6 +405,9 @@ public final class UserConnection implements ProxiedPlayer
@Override
public void sendMessage(ChatMessageType position, BaseComponent... message)
{
// transform score components
message = ChatComponentTransformer.getInstance().transform( this, message );
// Action bar doesn't display the new JSON formattings, legacy works - send it using this for now
if ( position == ChatMessageType.ACTION_BAR )
{
@@ -417,6 +421,8 @@ public final class UserConnection implements ProxiedPlayer
@Override
public void sendMessage(ChatMessageType position, BaseComponent message)
{
message = ChatComponentTransformer.getInstance().transform( this, message )[0];
// Action bar doesn't display the new JSON formattings, legacy works - send it using this for now
if ( position == ChatMessageType.ACTION_BAR )
{
@@ -594,23 +600,27 @@ public final class UserConnection implements ProxiedPlayer
return ImmutableMap.copyOf( forgeClientHandler.getClientModList() );
}
private static final String EMPTY_TEXT = ComponentSerializer.toString( new TextComponent( "" ) );
@Override
public void setTabHeader(BaseComponent header, BaseComponent footer)
{
header = ChatComponentTransformer.getInstance().transform( this, header )[0];
footer = ChatComponentTransformer.getInstance().transform( this, footer )[0];
unsafe().sendPacket( new PlayerListHeaderFooter(
( header != null ) ? ComponentSerializer.toString( header ) : EMPTY_TEXT,
( footer != null ) ? ComponentSerializer.toString( footer ) : EMPTY_TEXT
ComponentSerializer.toString( header ),
ComponentSerializer.toString( footer )
) );
}
@Override
public void setTabHeader(BaseComponent[] header, BaseComponent[] footer)
{
header = ChatComponentTransformer.getInstance().transform( this, header );
footer = ChatComponentTransformer.getInstance().transform( this, footer );
unsafe().sendPacket( new PlayerListHeaderFooter(
( header != null ) ? ComponentSerializer.toString( header ) : EMPTY_TEXT,
( footer != null ) ? ComponentSerializer.toString( footer ) : EMPTY_TEXT
ComponentSerializer.toString( header ),
ComponentSerializer.toString( footer )
) );
}
@@ -647,4 +657,10 @@ public final class UserConnection implements ProxiedPlayer
{
return !ch.isClosed();
}
@Override
public Scoreboard getScoreboard()
{
return serverSentScoreboard;
}
}

View File

@@ -0,0 +1,121 @@
package net.md_5.bungee.util;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.regex.Pattern;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ScoreComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.score.Score;
/**
* This class transforms chat components by attempting to replace transformable
* fields with the appropriate value.
* <br>
* ScoreComponents are transformed by replacing their
* {@link ScoreComponent#getName()}} into the matching entity's name as well as
* replacing the {@link ScoreComponent#getValue()} with the matching value in
* the {@link net.md_5.bungee.api.score.Scoreboard} if and only if the
* {@link ScoreComponent#getValue()} is not present.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class ChatComponentTransformer
{
private static final ChatComponentTransformer INSTANCE = new ChatComponentTransformer();
/**
* The Pattern to match entity selectors.
*/
private static final Pattern SELECTOR_PATTERN = Pattern.compile( "^@([pares])(?:\\[([^ ]*)\\])?$" );
public static ChatComponentTransformer getInstance()
{
return INSTANCE;
}
/**
* Transform a set of components, and attempt to transform the transformable
* fields. Entity selectors <b>cannot</b> be evaluated. This will
* recursively search for all extra components (see
* {@link BaseComponent#getExtra()}).
*
* @param player player
* @param component the component to transform
* @return the transformed component, or an array containing a single empty
* TextComponent if the components are null or empty
* @throws IllegalArgumentException if an entity selector pattern is present
*/
public BaseComponent[] transform(ProxiedPlayer player, BaseComponent... component)
{
if ( component == null || component.length < 1 )
{
return new BaseComponent[]
{
new TextComponent( "" )
};
}
for ( BaseComponent root : component )
{
if ( root.getExtra() != null && !root.getExtra().isEmpty() )
{
List<BaseComponent> list = Lists.newArrayList( transform( player, root.getExtra().toArray( new BaseComponent[ root.getExtra().size() ] ) ) );
root.setExtra( list );
}
if ( root instanceof ScoreComponent )
{
transformScoreComponent( player, (ScoreComponent) root );
}
}
return component;
}
/**
* Transform a ScoreComponent by replacing the name and value with the
* appropriate values.
*
* @param component the component to transform
* @param scoreboard the scoreboard to retrieve scores from
* @param player the player to use for the component's name
*/
private void transformScoreComponent(ProxiedPlayer player, ScoreComponent component)
{
Preconditions.checkArgument( !isSelectorPattern( component.getName() ), "Cannot transform entity selector patterns" );
if ( component.getValue() != null && !component.getValue().isEmpty() )
{
return; // pre-defined values override scoreboard values
}
// check for '*' wildcard
if ( component.getName().equals( "*" ) )
{
component.setName( player.getName() );
}
if ( player.getScoreboard().getObjective( component.getObjective() ) != null )
{
Score score = player.getScoreboard().getScore( component.getName() );
if ( score != null )
{
component.setValue( Integer.toString( score.getValue() ) );
}
}
}
/**
* Checks if the given string is an entity selector.
*
* @param pattern the pattern to check
* @return true if it is an entity selector
*/
public boolean isSelectorPattern(String pattern)
{
return SELECTOR_PATTERN.matcher( pattern ).matches();
}
}