6 Commits

Author SHA1 Message Date
a56ff2d4d2 new event TabCompleteRequestEvent and deprecate TabCompleteEvent 2024-03-18 15:56:30 +01:00
037cf6d97b Add CommandsDeclareEvent to declare commands with brigadier API 2024-03-18 15:56:30 +01:00
dc520b9bdf Server branding now includes the backend server name 2024-03-18 15:56:29 +01:00
b4095c4540 Multi-session with same Minecraft account with specific permission
Players with permission bungeecord.multiple_connect can have multiple connections with the same Minecraft account.
The UUID and player name is altered to avoid collision with other player:
UUID : xxxxxxxx-xxxx-VIxx-xxxx-xxxxxxxxxxxx
- The UUID version (V above) is now the provided version + 8 (for online player, it is 4, so it becomes C).
- The I digit will follow the index of the duplicated player : first duplicated player is 1, second one is 2.
- The name of the player will be the real player name, followed by the character "." (dot) followed by the duplication index.

Bedrock accounts connected using the Floodgate plugin will not be able to connect multiple times due to the risk of xUID collision.
2024-03-18 15:56:29 +01:00
c30c57482e Change projet configuration and POM for Pandacube 2024-03-18 15:56:29 +01:00
b13dcb3aa5 Remove modules and startup delay
We don’t need them for Pandacube
2024-03-18 15:56:29 +01:00
279 changed files with 3288 additions and 9401 deletions

View File

@@ -4,12 +4,12 @@ on: [push, pull_request]
jobs: jobs:
build: build:
runs-on: ubuntu-24.04 runs-on: ubuntu-22.04
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
java: [8, 11, 17, 21, 25-ea] java: [8, 11, 17, 21]
name: Java ${{ matrix.java }} name: Java ${{ matrix.java }}

View File

@@ -23,4 +23,4 @@ Binaries
-------- --------
Precompiled binaries are available for end users on [Jenkins](https://www.spigotmc.org/go/bungeecord-dl). Precompiled binaries are available for end users on [Jenkins](https://www.spigotmc.org/go/bungeecord-dl).
(c) 2012-2025 SpigotMC Pty. Ltd. (c) 2012-2023 SpigotMC Pty. Ltd.

View File

@@ -4,15 +4,14 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>net.md-5</groupId> <groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-parent</artifactId> <artifactId>bungeecord-parent</artifactId>
<version>1.21-R0.4-SNAPSHOT</version> <version>1.20-R0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId> <artifactId>bungeecord-api</artifactId>
<version>1.21-R0.4-SNAPSHOT</version> <version>1.20-R0.3-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>BungeeCord-API</name> <name>BungeeCord-API</name>
@@ -20,31 +19,25 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>net.md-5</groupId> <groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-chat</artifactId> <artifactId>bungeecord-chat</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.md-5</groupId> <groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-config</artifactId> <artifactId>bungeecord-config</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.md-5</groupId> <groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-dialog</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-event</artifactId> <artifactId>bungeecord-event</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.md-5</groupId> <groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-protocol</artifactId> <artifactId>bungeecord-protocol</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
<scope>compile</scope> <scope>compile</scope>

View File

@@ -16,7 +16,6 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.api.plugin.PluginManager; import net.md_5.bungee.api.plugin.PluginManager;
import net.md_5.bungee.api.scheduler.TaskScheduler; import net.md_5.bungee.api.scheduler.TaskScheduler;
import net.md_5.bungee.protocol.channel.BungeeChannelInitializer;
public abstract class ProxyServer public abstract class ProxyServer
{ {
@@ -312,56 +311,4 @@ public abstract class ProxyServer
*/ */
public abstract Title createTitle(); public abstract Title createTitle();
/**
* Get the unsafe methods of this class.
*
* @return the unsafe method interface
*/
public abstract Unsafe unsafe();
public interface Unsafe
{
/**
* Gets the frontend channel initializer
*
* @return the frontend channel initializer
*/
BungeeChannelInitializer getFrontendChannelInitializer();
/**
* Set the frontend channel initializer of this proxy
*
* @param channelInitializer the frontend channelInitializer to set
*/
void setFrontendChannelInitializer(BungeeChannelInitializer channelInitializer);
/**
* Gets the backend channel initializer
*
* @return the backend channel initializer
*/
BungeeChannelInitializer getBackendChannelInitializer();
/**
* Set the backend channel initializer of this proxy
*
* @param channelInitializer the backend channelInitializer to set
*/
void setBackendChannelInitializer(BungeeChannelInitializer channelInitializer);
/**
* Gets the server info channel initializer
*
* @return the server info channel initializer
*/
BungeeChannelInitializer getServerInfoChannelInitializer();
/**
* Set the server info channel initializer of this proxy
*
* @param channelInitializer the server info channelInitializer to set
*/
void setServerInfoChannelInitializer(BungeeChannelInitializer channelInitializer);
}
} }

View File

@@ -84,15 +84,5 @@ public interface Connection
* @param packet the packet to send * @param packet the packet to send
*/ */
void sendPacket(DefinedPacket packet); void sendPacket(DefinedPacket packet);
/**
* Queue a packet to this connection.
* If the packet is not registered for the connections current encoder protocol, it will be queued until it is,
* otherwise it will be sent immediately.
*
* @param packet the packet to be queued
* @throws UnsupportedOperationException if used for a PendingConnection
*/
void sendPacketQueued(DefinedPacket packet);
} }
} }

View File

@@ -113,18 +113,4 @@ public interface PendingConnection extends Connection
*/ */
@ApiStatus.Experimental @ApiStatus.Experimental
CompletableFuture<byte[]> retrieveCookie(String cookie); CompletableFuture<byte[]> retrieveCookie(String cookie);
/**
* Sends a login payload request to the client.
*
* @param channel the channel to send this data via
* @param data the data to send
* @return a {@link CompletableFuture} that will be completed when the Login
* Payload response is received. If the Vanilla client doesn't know the
* channel, the {@link CompletableFuture} will complete with a null value
* @throws IllegalStateException if the player's version is not at least
* 1.13
*/
@ApiStatus.Experimental
CompletableFuture<byte[]> sendData(String channel, byte[] data);
} }

View File

@@ -12,7 +12,6 @@ import net.md_5.bungee.api.SkinConfiguration;
import net.md_5.bungee.api.Title; import net.md_5.bungee.api.Title;
import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.dialog.Dialog;
import net.md_5.bungee.api.event.ServerConnectEvent; import net.md_5.bungee.api.event.ServerConnectEvent;
import net.md_5.bungee.api.score.Scoreboard; import net.md_5.bungee.api.score.Scoreboard;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
@@ -383,31 +382,4 @@ public interface ProxiedPlayer extends Connection, CommandSender
*/ */
@ApiStatus.Experimental @ApiStatus.Experimental
void transfer(String host, int port); void transfer(String host, int port);
/**
* Gets the client brand of this player.
* If the player has not sent a brand packet yet, it will return null.
*
* @return the brand of the client, or null if not received yet
*/
String getClientBrand();
/**
* Clear the player's open dialog.
*
* @throws IllegalStateException if the players version is not at least
* 1.21.6
*/
@ApiStatus.Experimental
void clearDialog();
/**
* Show a dialog to the player.
*
* @param dialog the dialog to show
* @throws IllegalStateException if the players version is not at least
* 1.21.6
*/
@ApiStatus.Experimental
void showDialog(Dialog dialog);
} }

View File

@@ -0,0 +1,157 @@
package net.md_5.bungee.api.event;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.RootCommandNode;
import lombok.AccessLevel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Setter;
import lombok.ToString;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.connection.Connection;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.api.plugin.PluginManager;
import net.md_5.bungee.api.plugin.TabExecutor;
/**
* Event called when a downstream server (on 1.13+) sends the command structure
* to a player, but before BungeeCord adds the dummy command nodes of
* registered commands.
* <p>
* BungeeCord will not overwrite the modifications made by the listeners.
*
* <h2>Usage example</h2>
* Here is a usage example of this event, to declare a command structure.
* This illustrates the commands /server and /send of Bungee.
* <pre>
* event.getRoot().addChild( LiteralArgumentBuilder.&lt;CommandSender&gt;literal( "server" )
* .requires( sender -&gt; sender.hasPermission( "bungeecord.command.server" ) )
* .executes( a -&gt; 0 )
* .then( RequiredArgumentBuilder.argument( "serverName", StringArgumentType.greedyString() )
* .suggests( SuggestionRegistry.ASK_SERVER )
* )
* .build()
* );
* event.getRoot().addChild( LiteralArgumentBuilder.&lt;CommandSender&gt;literal( "send" )
* .requires( sender -&gt; sender.hasPermission( "bungeecord.command.send" ) )
* .then( RequiredArgumentBuilder.argument( "playerName", StringArgumentType.word() )
* .suggests( SuggestionRegistry.ASK_SERVER )
* .then( RequiredArgumentBuilder.argument( "serverName", StringArgumentType.greedyString() )
* .suggests( SuggestionRegistry.ASK_SERVER )
* )
* )
* .build()
* );
* </pre>
*
* <h2>Flag a {@link CommandNode} as executable or not</h2>
* The implementation of a {@link com.mojang.brigadier.Command Command} used in
* {@link ArgumentBuilder#executes(com.mojang.brigadier.Command)} will never be
* executed. This will only tell to the client if the current node is
* executable or not.
* <ul>
* <li>
* {@code builder.executes(null)} (default) to mark the node as not
* executable.
* </li>
* <li>
* {@code builder.executes(a -> 0)}, or any non null argument, to mark
* the node as executable (the child arguments are displayed as
* optional).
* </li>
* </ul>
*
* <h2>{@link CommandNode}s suggestions management</h2>
* The implementation of a SuggestionProvider used in
* {@link RequiredArgumentBuilder#suggests(SuggestionProvider)} will never be
* executed. This will only tell to the client how to deal with the
* auto-completion of the argument.
* <ul>
* <li>
* {@code builder.suggests(null)} (default) to disable auto-completion
* for this argument.
* </li>
* <li>
* {@code builder.suggests(SuggestionRegistry.ALL_RECIPES)} to suggest
* Minecrafts recipes.
* </li>
* <li>
* {@code builder.suggests(SuggestionRegistry.AVAILABLE_SOUNDS)} to
* suggest Minecrafts default sound identifiers.
* </li>
* <li>
* {@code builder.suggests(SuggestionRegistry.SUMMONABLE_ENTITIES)} to
* suggest Minecrafts default summonable entities identifiers.
* </li>
* <li>
* {@code builder.suggests(SuggestionRegistry.ASK_SERVER)}, or any
* other non null argument, to make the Minecraft client ask
* auto-completion to the server. Any specified implementation of
* {@link SuggestionProvider} will never be executed.
* </li>
* </ul>
*
* <h2>Argument types</h2>
* When building a new argument command node using
* {@link RequiredArgumentBuilder#argument(String, ArgumentType)}, you have to
* specify an {@link ArgumentType}. You can use all subclasses of
* {@link ArgumentType} provided with brigadier (for instance,
* {@link StringArgumentType} or {@link IntegerArgumentType}), or call any
* {@code ArgumentRegistry.minecraft*()} methods to use a {@code minecraft:*}
* argument type.
*
* <h2>Limitations with brigadier API</h2>
* This event is only used for the client to show command syntax, suggest
* sub-commands and color the arguments in the chat box. The command execution
* needs to be implemented using {@link PluginManager#registerCommand(Plugin,
* Command)} and the server-side tab-completion using {@link TabCompleteEvent}
* or {@link TabExecutor}.
*/
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class CommandsDeclareEvent extends TargetedEvent
{
/**
* Wether or not the command tree is modified by this event.
*
* If this value is set to true, BungeeCord will ensure that the
* modifications made in the command tree, will be sent to the player.
* If this is false, the modifications may not be taken into account.
*
* When calling {@link #getRoot()}, this value is automatically set
* to true.
*/
@Setter(value = AccessLevel.NONE)
private boolean modified = false;
/**
* The root command node of the command structure that will be send to the
* player.
*/
private final RootCommandNode<CommandSender> root;
public CommandsDeclareEvent(Connection sender, Connection receiver, RootCommandNode<CommandSender> root)
{
super( sender, receiver );
this.root = root;
}
/**
* The root command node of the command structure that will be send to the
* player.
* @return The root command node
*/
public RootCommandNode<CommandSender> getRoot()
{
modified = true;
return root;
}
}

View File

@@ -1,39 +0,0 @@
package net.md_5.bungee.api.event;
import com.google.gson.JsonElement;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Cancellable;
import net.md_5.bungee.api.plugin.Event;
import org.jetbrains.annotations.ApiStatus;
/**
* Called after a {@link ProxiedPlayer} runs a custom action from a chat event
* or form submission.
*/
@Data
@ToString(callSuper = false)
@EqualsAndHashCode(callSuper = false)
@ApiStatus.Experimental
public class CustomClickEvent extends Event implements Cancellable
{
/**
* Player who clicked.
*/
private final ProxiedPlayer player;
/**
* Custom action ID.
*/
private final String id;
/**
* The data as submitted.
*/
private final JsonElement data;
/**
* Cancelled state.
*/
private boolean cancelled;
}

View File

@@ -3,9 +3,8 @@ package net.md_5.bungee.api.event;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
import net.md_5.bungee.api.Callback;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Event;
/** /**
* Event called as soon as a connection has a {@link ProxiedPlayer} and is ready * Event called as soon as a connection has a {@link ProxiedPlayer} and is ready
@@ -14,22 +13,11 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
@Data @Data
@ToString(callSuper = false) @ToString(callSuper = false)
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
public class PostLoginEvent extends AsyncEvent<PostLoginEvent> public class PostLoginEvent extends Event
{ {
/** /**
* The player involved with this event. * The player involved with this event.
*/ */
private final ProxiedPlayer player; private final ProxiedPlayer player;
/**
* The server to which the player will initially be connected.
*/
private ServerInfo target;
public PostLoginEvent(ProxiedPlayer player, ServerInfo target, Callback<PostLoginEvent> done)
{
super( done );
this.player = player;
this.target = target;
}
} }

View File

@@ -3,7 +3,6 @@ package net.md_5.bungee.api.event;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.ToString;
import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.plugin.Event; import net.md_5.bungee.api.plugin.Event;
@@ -11,7 +10,6 @@ import net.md_5.bungee.api.plugin.Event;
* Called when somebody reloads BungeeCord * Called when somebody reloads BungeeCord
*/ */
@Getter @Getter
@ToString(callSuper = false)
@AllArgsConstructor @AllArgsConstructor
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
public class ProxyReloadEvent extends Event public class ProxyReloadEvent extends Event

View File

@@ -9,7 +9,9 @@ import net.md_5.bungee.api.plugin.Cancellable;
/** /**
* Event called when a player uses tab completion. * Event called when a player uses tab completion.
* @deprecated please use {@link TabCompleteRequestEvent} to support 1.13+ suggestions.
*/ */
@Deprecated
@Data @Data
@ToString(callSuper = true) @ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)

View File

@@ -0,0 +1,85 @@
package net.md_5.bungee.api.event;
import com.google.common.base.Preconditions;
import com.mojang.brigadier.context.StringRange;
import com.mojang.brigadier.suggestion.Suggestions;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import net.md_5.bungee.api.connection.Connection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Cancellable;
import net.md_5.bungee.protocol.ProtocolConstants;
/**
* Event called when a player uses tab completion.
*/
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class TabCompleteRequestEvent extends TargetedEvent implements Cancellable
{
/**
* Cancelled state.
*/
private boolean cancelled;
/**
* The message the player has already entered.
*/
private final String cursor;
/**
* Range corresponding to the last word of {@link #getCursor()}.
* If you want your suggestions to be compatible with 1.12 and older
* clients, you need to {@link #setSuggestions(Suggestions)} with
* a range equals to this one.
* For 1.13 and newer clients, any other range that cover any part of
* {@link #getCursor()} is fine.<br>
* To check if the client supports custom ranges, use
* {@link #supportsCustomRange()}.
*/
private final StringRange legacyCompatibleRange;
/**
* The suggestions that will be sent to the client. If this list is empty,
* the request will be forwarded to the server.
*/
private Suggestions suggestions;
public TabCompleteRequestEvent(Connection sender, Connection receiver, String cursor, StringRange legacyCompatibleRange, Suggestions suggestions)
{
super( sender, receiver );
this.cursor = cursor;
this.legacyCompatibleRange = legacyCompatibleRange;
this.suggestions = suggestions;
}
/**
* Sets the suggestions that will be sent to the client.
* If this list is empty, the request will be forwarded to the server.
* @param suggestions the new Suggestions. Cannot be null.
* @throws IllegalArgumentException if the client is on 1.12 or lower and
* {@code suggestions.getRange()} is not equals to {@link #legacyCompatibleRange}.
*/
public void setSuggestions(Suggestions suggestions)
{
Preconditions.checkNotNull( suggestions );
Preconditions.checkArgument( supportsCustomRange() || legacyCompatibleRange.equals( suggestions.getRange() ),
"Clients on 1.12 or lower versions don't support the provided range for tab-completion: " + suggestions.getRange()
+ ". Please use TabCompleteRequestEvent.getLegacyCompatibleRange() for legacy clients." );
this.suggestions = suggestions;
}
/**
* Convenient method to tell if the client supports custom range for
* suggestions.
* If the client is on 1.13 or above, this methods returns true, and any
* range can be used for {@link #setSuggestions(Suggestions)}. Otherwise,
* it returns false and the defined range must be equals to
* {@link #legacyCompatibleRange}.
* @return true if the client is on 1.13 or newer version, false otherwise.
*/
public boolean supportsCustomRange()
{
return ( (ProxiedPlayer) getSender() ).getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_13;
}
}

View File

@@ -35,7 +35,6 @@ import org.eclipse.aether.transport.http.HttpTransporterFactory;
class LibraryLoader class LibraryLoader
{ {
private static final String REPOSITORY_PROPERTY = "net.md_5.bungee.api.plugin.centralURL";
private final Logger logger; private final Logger logger;
private final RepositorySystem repository; private final RepositorySystem repository;
private final DefaultRepositorySystemSession session; private final DefaultRepositorySystemSession session;
@@ -62,14 +61,9 @@ class LibraryLoader
logger.log( Level.INFO, "Downloading {0}", event.getResource().getRepositoryUrl() + event.getResource().getResourceName() ); logger.log( Level.INFO, "Downloading {0}", event.getResource().getRepositoryUrl() + event.getResource().getResourceName() );
} }
} ); } );
// SPIGOT-7638: Add system properties,
// since JdkVersionProfileActivator needs 'java.version' when a profile has the 'jdk' element
// otherwise it will silently fail and not resolves the dependencies in the affected pom.
session.setSystemProperties( System.getProperties() );
session.setReadOnly(); session.setReadOnly();
this.repositories = repository.newResolutionRepositories( session, Arrays.asList( new RemoteRepository.Builder( "central", "default", System.getProperty( REPOSITORY_PROPERTY, "https://repo.maven.apache.org/maven2" ) ).build() ) ); this.repositories = repository.newResolutionRepositories( session, Arrays.asList( new RemoteRepository.Builder( "central", "default", "https://repo.maven.apache.org/maven2" ).build() ) );
} }
public ClassLoader createLoader(PluginDescription desc) public ClassLoader createLoader(PluginDescription desc)

View File

@@ -23,10 +23,6 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.Stack; import java.util.Stack;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import java.util.logging.Level; import java.util.logging.Level;
@@ -63,9 +59,6 @@ public final class PluginManager
private final Multimap<Plugin, Command> commandsByPlugin = ArrayListMultimap.create(); private final Multimap<Plugin, Command> commandsByPlugin = ArrayListMultimap.create();
private final Multimap<Plugin, Listener> listenersByPlugin = ArrayListMultimap.create(); private final Multimap<Plugin, Listener> listenersByPlugin = ArrayListMultimap.create();
private final ReadWriteLock commandsLock = new ReentrantReadWriteLock();
private final Lock listenersLock = new ReentrantLock();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public PluginManager(ProxyServer proxy) public PluginManager(ProxyServer proxy)
{ {
@@ -99,9 +92,6 @@ public final class PluginManager
* @param command the command to register * @param command the command to register
*/ */
public void registerCommand(Plugin plugin, Command command) public void registerCommand(Plugin plugin, Command command)
{
commandsLock.writeLock().lock();
try
{ {
commandMap.put( command.getName().toLowerCase( Locale.ROOT ), command ); commandMap.put( command.getName().toLowerCase( Locale.ROOT ), command );
for ( String alias : command.getAliases() ) for ( String alias : command.getAliases() )
@@ -109,10 +99,6 @@ public final class PluginManager
commandMap.put( alias.toLowerCase( Locale.ROOT ), command ); commandMap.put( alias.toLowerCase( Locale.ROOT ), command );
} }
commandsByPlugin.put( plugin, command ); commandsByPlugin.put( plugin, command );
} finally
{
commandsLock.writeLock().unlock();
}
} }
/** /**
@@ -121,16 +107,9 @@ public final class PluginManager
* @param command the command to unregister * @param command the command to unregister
*/ */
public void unregisterCommand(Command command) public void unregisterCommand(Command command)
{
commandsLock.writeLock().lock();
try
{ {
while ( commandMap.values().remove( command ) ); while ( commandMap.values().remove( command ) );
commandsByPlugin.values().remove( command ); commandsByPlugin.values().remove( command );
} finally
{
commandsLock.writeLock().unlock();
}
} }
/** /**
@@ -139,9 +118,6 @@ public final class PluginManager
* @param plugin the plugin to register the commands of * @param plugin the plugin to register the commands of
*/ */
public void unregisterCommands(Plugin plugin) public void unregisterCommands(Plugin plugin)
{
commandsLock.writeLock().lock();
try
{ {
for ( Iterator<Command> it = commandsByPlugin.get( plugin ).iterator(); it.hasNext(); ) for ( Iterator<Command> it = commandsByPlugin.get( plugin ).iterator(); it.hasNext(); )
{ {
@@ -149,10 +125,6 @@ public final class PluginManager
while ( commandMap.values().remove( command ) ); while ( commandMap.values().remove( command ) );
it.remove(); it.remove();
} }
} finally
{
commandsLock.writeLock().unlock();
}
} }
private Command getCommandIfEnabled(String commandName, CommandSender sender) private Command getCommandIfEnabled(String commandName, CommandSender sender)
@@ -165,14 +137,7 @@ public final class PluginManager
return null; return null;
} }
commandsLock.readLock().lock();
try
{
return commandMap.get( commandLower ); return commandMap.get( commandLower );
} finally
{
commandsLock.readLock().unlock();
}
} }
/** /**
@@ -468,9 +433,6 @@ public final class PluginManager
* @param listener the listener to register events for * @param listener the listener to register events for
*/ */
public void registerListener(Plugin plugin, Listener listener) public void registerListener(Plugin plugin, Listener listener)
{
listenersLock.lock();
try
{ {
for ( Method method : listener.getClass().getDeclaredMethods() ) for ( Method method : listener.getClass().getDeclaredMethods() )
{ {
@@ -479,10 +441,6 @@ public final class PluginManager
} }
eventBus.register( listener ); eventBus.register( listener );
listenersByPlugin.put( plugin, listener ); listenersByPlugin.put( plugin, listener );
} finally
{
listenersLock.unlock();
}
} }
/** /**
@@ -491,16 +449,9 @@ public final class PluginManager
* @param listener the listener to unregister * @param listener the listener to unregister
*/ */
public void unregisterListener(Listener listener) public void unregisterListener(Listener listener)
{
listenersLock.lock();
try
{ {
eventBus.unregister( listener ); eventBus.unregister( listener );
listenersByPlugin.values().remove( listener ); listenersByPlugin.values().remove( listener );
} finally
{
listenersLock.unlock();
}
} }
/** /**
@@ -509,19 +460,12 @@ public final class PluginManager
* @param plugin target plugin * @param plugin target plugin
*/ */
public void unregisterListeners(Plugin plugin) public void unregisterListeners(Plugin plugin)
{
listenersLock.lock();
try
{ {
for ( Iterator<Listener> it = listenersByPlugin.get( plugin ).iterator(); it.hasNext(); ) for ( Iterator<Listener> it = listenersByPlugin.get( plugin ).iterator(); it.hasNext(); )
{ {
eventBus.unregister( it.next() ); eventBus.unregister( it.next() );
it.remove(); it.remove();
} }
} finally
{
listenersLock.unlock();
}
} }
/** /**
@@ -530,15 +474,8 @@ public final class PluginManager
* @return commands * @return commands
*/ */
public Collection<Map.Entry<String, Command>> getCommands() public Collection<Map.Entry<String, Command>> getCommands()
{
commandsLock.readLock().lock();
try
{ {
return Collections.unmodifiableCollection( commandMap.entrySet() ); return Collections.unmodifiableCollection( commandMap.entrySet() );
} finally
{
commandsLock.readLock().unlock();
}
} }
boolean isTransitiveDepend(PluginDescription plugin, PluginDescription depend) boolean isTransitiveDepend(PluginDescription plugin, PluginDescription depend)

View File

@@ -1,37 +1,22 @@
package net.md_5.bungee.util; package net.md_5.bungee.util;
import it.unimi.dsi.fastutil.Hash; import gnu.trove.strategy.HashingStrategy;
import java.util.Locale; import java.util.Locale;
class CaseInsensitiveHashingStrategy implements Hash.Strategy<String> class CaseInsensitiveHashingStrategy implements HashingStrategy
{ {
static final CaseInsensitiveHashingStrategy INSTANCE = new CaseInsensitiveHashingStrategy(); static final CaseInsensitiveHashingStrategy INSTANCE = new CaseInsensitiveHashingStrategy();
@Override @Override
public int hashCode(String object) public int computeHashCode(Object object)
{ {
if ( object == null ) return ( (String) object ).toLowerCase( Locale.ROOT ).hashCode();
{
return 0;
}
return object.toLowerCase( Locale.ROOT ).hashCode();
} }
@Override @Override
public boolean equals(String o1, String o2) public boolean equals(Object o1, Object o2)
{ {
if ( o1 == o2 ) return o1.equals( o2 ) || ( o1 instanceof String && o2 instanceof String && ( (String) o1 ).toLowerCase( Locale.ROOT ).equals( ( (String) o2 ).toLowerCase( Locale.ROOT ) ) );
{
return true;
}
if ( o1 == null || o2 == null )
{
return false;
}
return o1.equals( o2 ) || o1.toLowerCase( Locale.ROOT ).equals( o2.toLowerCase( Locale.ROOT ) );
} }
} }

View File

@@ -1,9 +1,9 @@
package net.md_5.bungee.util; package net.md_5.bungee.util;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; import gnu.trove.map.hash.TCustomHashMap;
import java.util.Map; import java.util.Map;
public class CaseInsensitiveMap<V> extends Object2ObjectOpenCustomHashMap<String, V> public class CaseInsensitiveMap<V> extends TCustomHashMap<String, V>
{ {
public CaseInsensitiveMap() public CaseInsensitiveMap()
@@ -13,6 +13,6 @@ public class CaseInsensitiveMap<V> extends Object2ObjectOpenCustomHashMap<String
public CaseInsensitiveMap(Map<? extends String, ? extends V> map) public CaseInsensitiveMap(Map<? extends String, ? extends V> map)
{ {
super( map, CaseInsensitiveHashingStrategy.INSTANCE ); super( CaseInsensitiveHashingStrategy.INSTANCE, map );
} }
} }

View File

@@ -1,9 +1,9 @@
package net.md_5.bungee.util; package net.md_5.bungee.util;
import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet; import gnu.trove.set.hash.TCustomHashSet;
import java.util.Collection; import java.util.Collection;
public class CaseInsensitiveSet extends ObjectOpenCustomHashSet<String> public class CaseInsensitiveSet extends TCustomHashSet<String>
{ {
public CaseInsensitiveSet() public CaseInsensitiveSet()
@@ -13,6 +13,6 @@ public class CaseInsensitiveSet extends ObjectOpenCustomHashSet<String>
public CaseInsensitiveSet(Collection<? extends String> collection) public CaseInsensitiveSet(Collection<? extends String> collection)
{ {
super( collection, CaseInsensitiveHashingStrategy.INSTANCE ); super( CaseInsensitiveHashingStrategy.INSTANCE, collection );
} }
} }

View File

@@ -13,12 +13,12 @@ public class CaseInsensitiveTest
CaseInsensitiveMap<Object> map = new CaseInsensitiveMap<>(); CaseInsensitiveMap<Object> map = new CaseInsensitiveMap<>();
map.put( "FOO", obj ); map.put( "FOO", obj );
assertTrue( map.containsKey( "foo" ) ); // Assert that contains is case insensitive assertTrue( map.contains( "foo" ) ); // Assert that contains is case insensitive
assertTrue( map.entrySet().iterator().next().getKey().equals( "FOO" ) ); // Assert that case is preserved assertTrue( map.entrySet().iterator().next().getKey().equals( "FOO" ) ); // Assert that case is preserved
// Assert that remove is case insensitive // Assert that remove is case insensitive
map.remove( "FoO" ); map.remove( "FoO" );
assertFalse( map.containsKey( "foo" ) ); assertFalse( map.contains( "foo" ) );
} }
@Test @Test

View File

@@ -4,15 +4,14 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>net.md-5</groupId> <groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-parent</artifactId> <artifactId>bungeecord-parent</artifactId>
<version>1.21-R0.4-SNAPSHOT</version> <version>1.20-R0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-bootstrap</artifactId> <artifactId>bungeecord-bootstrap</artifactId>
<version>1.21-R0.4-SNAPSHOT</version> <version>1.20-R0.3-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>BungeeCord-Bootstrap</name> <name>BungeeCord-Bootstrap</name>
@@ -21,14 +20,12 @@
<properties> <properties>
<maven.deploy.skip>true</maven.deploy.skip> <maven.deploy.skip>true</maven.deploy.skip>
<maven.javadoc.skip>true</maven.javadoc.skip> <maven.javadoc.skip>true</maven.javadoc.skip>
<maven.compiler.source>1.6</maven.compiler.source>
<maven.compiler.target>1.6</maven.compiler.target>
<maven.build.timestamp.format>yyyyMMdd</maven.build.timestamp.format> <maven.build.timestamp.format>yyyyMMdd</maven.build.timestamp.format>
</properties> </properties>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>net.md-5</groupId> <groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-proxy</artifactId> <artifactId>bungeecord-proxy</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
<scope>compile</scope> <scope>compile</scope>
@@ -36,19 +33,18 @@
</dependencies> </dependencies>
<build> <build>
<finalName>BungeeCord</finalName> <finalName>BungeeCord-${project.version}-${build.number}</finalName>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
<version>3.4.1</version> <version>3.3.0</version>
<configuration> <configuration>
<archive> <archive>
<manifestEntries> <manifestEntries>
<Main-Class>net.md_5.bungee.Bootstrap</Main-Class> <Main-Class>net.md_5.bungee.Bootstrap</Main-Class>
<Implementation-Version>${describe}</Implementation-Version> <Implementation-Version>${describe}</Implementation-Version>
<Specification-Version>${maven.build.timestamp}</Specification-Version> <Specification-Version>${maven.build.timestamp}</Specification-Version>
<Enable-Native-Access>ALL-UNNAMED</Enable-Native-Access>
</manifestEntries> </manifestEntries>
</archive> </archive>
</configuration> </configuration>
@@ -56,7 +52,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>3.5.3</version> <version>3.5.2</version>
<executions> <executions>
<execution> <execution>
<phase>package</phase> <phase>package</phase>

View File

@@ -4,15 +4,14 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>net.md-5</groupId> <groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-parent</artifactId> <artifactId>bungeecord-parent</artifactId>
<version>1.21-R0.4-SNAPSHOT</version> <version>1.20-R0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-chat</artifactId> <artifactId>bungeecord-chat</artifactId>
<version>1.21-R0.4-SNAPSHOT</version> <version>1.20-R0.3-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>BungeeCord-Chat</name> <name>BungeeCord-Chat</name>
@@ -22,7 +21,7 @@
<dependency> <dependency>
<groupId>com.google.code.gson</groupId> <groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId> <artifactId>gson</artifactId>
<version>2.11.0</version> <version>2.10.1</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@@ -244,7 +244,7 @@ public final class ChatColor
public static ChatColor of(String string) public static ChatColor of(String string)
{ {
Preconditions.checkArgument( string != null, "string cannot be null" ); Preconditions.checkArgument( string != null, "string cannot be null" );
if ( string.length() == 7 && string.charAt( 0 ) == '#' ) if ( string.startsWith( "#" ) && string.length() == 7 )
{ {
int rgb; int rgb;
try try

View File

@@ -1,10 +1,8 @@
package net.md_5.bungee.api.chat; package net.md_5.bungee.api.chat;
import java.awt.Color;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@@ -131,10 +129,6 @@ public abstract class BaseComponent
{ {
setColor( component.getColorRaw() ); setColor( component.getColorRaw() );
} }
if ( replace || !style.hasShadowColor() )
{
setShadowColor( component.getShadowColorRaw() );
}
if ( replace || !style.hasFont() ) if ( replace || !style.hasFont() )
{ {
setFont( component.getFontRaw() ); setFont( component.getFontRaw() );
@@ -181,7 +175,6 @@ public abstract class BaseComponent
if ( retention == FormatRetention.EVENTS || retention == FormatRetention.NONE ) if ( retention == FormatRetention.EVENTS || retention == FormatRetention.NONE )
{ {
setColor( null ); setColor( null );
setShadowColor( null );
setBold( null ); setBold( null );
setItalic( null ); setItalic( null );
setUnderlined( null ); setUnderlined( null );
@@ -260,9 +253,6 @@ public abstract class BaseComponent
/** /**
* Set this component's color. * Set this component's color.
* <p>
* <b>Warning: This should be a color, not formatting code (ie,
* {@link ChatColor#color} should not be null).</b>
* *
* @param color the component color, or null to use the default * @param color the component color, or null to use the default
*/ */
@@ -302,46 +292,6 @@ public abstract class BaseComponent
return style.getColor(); return style.getColor();
} }
/**
* Set this component's shadow color.
*
* @param color the component shadow color, or null to use the default
*/
public void setShadowColor(Color color)
{
this.style.setShadowColor( color );
}
/**
* Returns the shadow color of this component. This uses the parent's shadow color if this
* component doesn't have one. null is returned if no shadow color is found.
*
* @return the shadow color of this component
*/
public Color getShadowColor()
{
if ( !style.hasShadowColor() )
{
if ( parent == null )
{
return null;
}
return parent.getShadowColor();
}
return style.getShadowColor();
}
/**
* Returns the shadow color of this component without checking the parents
* shadow color. May return null
*
* @return the shadow color of this component
*/
public Color getShadowColorRaw()
{
return style.getShadowColor();
}
/** /**
* Set this component's font. * Set this component's font.
* *
@@ -583,10 +533,6 @@ public abstract class BaseComponent
{ {
setColor( style.getColor() ); setColor( style.getColor() );
} }
if ( style.hasShadowColor() )
{
setShadowColor( style.getShadowColor() );
}
if ( style.hasFont() ) if ( style.hasFont() )
{ {
setFont( style.getFont() ); setFont( style.getFont() );
@@ -678,11 +624,11 @@ public abstract class BaseComponent
public String toPlainText() public String toPlainText()
{ {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
toPlainText( new LimitedStringVisitor( builder, Short.MAX_VALUE ) ); toPlainText( builder );
return builder.toString(); return builder.toString();
} }
void toPlainText(StringVisitor builder) void toPlainText(StringBuilder builder)
{ {
if ( extra != null ) if ( extra != null )
{ {
@@ -702,11 +648,11 @@ public abstract class BaseComponent
public String toLegacyText() public String toLegacyText()
{ {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
toLegacyText( new LimitedStringVisitor( builder, Short.MAX_VALUE ) ); toLegacyText( builder );
return builder.toString(); return builder.toString();
} }
void toLegacyText(StringVisitor builder) void toLegacyText(StringBuilder builder)
{ {
if ( extra != null ) if ( extra != null )
{ {
@@ -717,7 +663,7 @@ public abstract class BaseComponent
} }
} }
void addFormat(StringVisitor builder) void addFormat(StringBuilder builder)
{ {
builder.append( getColor() ); builder.append( getColor() );
if ( isBold() ) if ( isBold() )
@@ -741,35 +687,4 @@ public abstract class BaseComponent
builder.append( ChatColor.MAGIC ); builder.append( ChatColor.MAGIC );
} }
} }
@FunctionalInterface
protected static interface StringVisitor
{
void append(String s);
default void append(Object obj)
{
append( String.valueOf( obj ) );
}
}
@Data
protected static class LimitedStringVisitor implements StringVisitor
{
private final StringBuilder builder;
private final int maxLength;
@Override
public void append(String s)
{
if ( builder.length() >= maxLength )
{
throw new IllegalArgumentException( "String exceeded maximum length " + maxLength );
}
builder.append( s );
}
}
} }

View File

@@ -4,14 +4,12 @@ import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.ToString; import lombok.ToString;
import org.jetbrains.annotations.ApiStatus;
@Getter @Getter
@ToString @ToString
@EqualsAndHashCode @EqualsAndHashCode
@RequiredArgsConstructor @RequiredArgsConstructor
@ApiStatus.NonExtendable public final class ClickEvent
public class ClickEvent
{ {
/** /**
@@ -54,19 +52,11 @@ public class ClickEvent
* {@link net.md_5.bungee.api.chat.ClickEvent#value} in a book. * {@link net.md_5.bungee.api.chat.ClickEvent#value} in a book.
*/ */
CHANGE_PAGE, CHANGE_PAGE,
/**
* Must use subclass ShowDialogClickEvent.
*/
SHOW_DIALOG,
/** /**
* Copy the string given by * Copy the string given by
* {@link net.md_5.bungee.api.chat.ClickEvent#value} into the player's * {@link net.md_5.bungee.api.chat.ClickEvent#value} into the player's
* clipboard. * clipboard.
*/ */
COPY_TO_CLIPBOARD, COPY_TO_CLIPBOARD
/**
* Must use subclass {@link ClickEventCustom}.
*/
CUSTOM,
} }
} }

View File

@@ -1,30 +0,0 @@
package net.md_5.bungee.api.chat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* Click event which sends a custom payload to the server.
*/
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class ClickEventCustom extends ClickEvent
{
/**
* The custom payload.
*/
private final String payload;
/**
* @param id identifier for the event (lower case, no special characters)
* @param payload custom payload
*/
public ClickEventCustom(String id, String payload)
{
super( ClickEvent.Action.CUSTOM, id );
this.payload = payload;
}
}

View File

@@ -1,6 +1,5 @@
package net.md_5.bungee.api.chat; package net.md_5.bungee.api.chat;
import java.awt.Color;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@@ -19,15 +18,8 @@ public final class ComponentStyle implements Cloneable
/** /**
* The color of this style. * The color of this style.
* <p>
* <b>Warning: This should be a color, not formatting code (ie,
* {@link ChatColor#color} should not be null).</b>
*/ */
private ChatColor color; private ChatColor color;
/**
* The shadow color of this style.
*/
private Color shadowColor;
/** /**
* The font of this style. * The font of this style.
*/ */
@@ -73,26 +65,6 @@ public final class ComponentStyle implements Cloneable
return ( color != null ); return ( color != null );
} }
/**
* Returns the shadow color of this style. May return null.
*
* @return the shadow color of this style, or null if default color
*/
public Color getShadowColor()
{
return shadowColor;
}
/**
* Returns whether or not this style has a shadow color set.
*
* @return whether a shadow color is set
*/
public boolean hasShadowColor()
{
return ( shadowColor != null );
}
/** /**
* Returns the font of this style. May return null. * Returns the font of this style. May return null.
* *
@@ -220,7 +192,7 @@ public final class ComponentStyle implements Cloneable
*/ */
public boolean isEmpty() public boolean isEmpty()
{ {
return color == null && shadowColor == null && font == null && bold == null return color == null && font == null && bold == null
&& italic == null && underlined == null && italic == null && underlined == null
&& strikethrough == null && obfuscated == null; && strikethrough == null && obfuscated == null;
} }
@@ -228,7 +200,7 @@ public final class ComponentStyle implements Cloneable
@Override @Override
public ComponentStyle clone() public ComponentStyle clone()
{ {
return new ComponentStyle( color, shadowColor, font, bold, italic, underlined, strikethrough, obfuscated ); return new ComponentStyle( color, font, bold, italic, underlined, strikethrough, obfuscated );
} }
/** /**
@@ -252,7 +224,6 @@ public final class ComponentStyle implements Cloneable
{ {
return new ComponentStyleBuilder() return new ComponentStyleBuilder()
.color( other.color ) .color( other.color )
.shadowColor( other.shadowColor )
.font( other.font ) .font( other.font )
.bold( other.bold ) .bold( other.bold )
.italic( other.italic ) .italic( other.italic )

View File

@@ -1,6 +1,5 @@
package net.md_5.bungee.api.chat; package net.md_5.bungee.api.chat;
import java.awt.Color;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
/** /**
@@ -27,7 +26,6 @@ public final class ComponentStyleBuilder
{ {
private ChatColor color; private ChatColor color;
private Color shadowColor;
private String font; private String font;
private Boolean bold, italic, underlined, strikethrough, obfuscated; private Boolean bold, italic, underlined, strikethrough, obfuscated;
@@ -43,18 +41,6 @@ public final class ComponentStyleBuilder
return this; return this;
} }
/**
* Set the style shadow color.
*
* @param shadowColor the shadow color to set, or null to use the default
* @return this ComponentStyleBuilder for chaining
*/
public ComponentStyleBuilder shadowColor(Color shadowColor)
{
this.shadowColor = shadowColor;
return this;
}
/** /**
* Set the style font. * Set the style font.
* *
@@ -135,6 +121,6 @@ public final class ComponentStyleBuilder
*/ */
public ComponentStyle build() public ComponentStyle build()
{ {
return new ComponentStyle( color, shadowColor, font, bold, italic, underlined, strikethrough, obfuscated ); return new ComponentStyle( color, font, bold, italic, underlined, strikethrough, obfuscated );
} }
} }

View File

@@ -13,7 +13,7 @@ import net.md_5.bungee.api.chat.hover.content.Content;
import net.md_5.bungee.api.chat.hover.content.Entity; import net.md_5.bungee.api.chat.hover.content.Entity;
import net.md_5.bungee.api.chat.hover.content.Item; import net.md_5.bungee.api.chat.hover.content.Item;
import net.md_5.bungee.api.chat.hover.content.Text; import net.md_5.bungee.api.chat.hover.content.Text;
import org.jetbrains.annotations.ApiStatus; import net.md_5.bungee.chat.ComponentSerializer;
@Getter @Getter
@ToString @ToString
@@ -34,7 +34,6 @@ public final class HoverEvent
* Returns whether this hover event is prior to 1.16 * Returns whether this hover event is prior to 1.16
*/ */
@Setter @Setter
@ApiStatus.Internal
private boolean legacy = false; private boolean legacy = false;
/** /**
@@ -72,6 +71,22 @@ public final class HoverEvent
this.legacy = true; this.legacy = true;
} }
@Deprecated
public BaseComponent[] getValue()
{
Content content = contents.get( 0 );
if ( content instanceof Text && ( (Text) content ).getValue() instanceof BaseComponent[] )
{
return (BaseComponent[]) ( (Text) content ).getValue();
}
TextComponent component = new TextComponent( ComponentSerializer.toString( content ) );
return new BaseComponent[]
{
component
};
}
/** /**
* Adds a content to this hover event. * Adds a content to this hover event.
* *

View File

@@ -50,14 +50,14 @@ public final class KeybindComponent extends BaseComponent
} }
@Override @Override
protected void toPlainText(StringVisitor builder) protected void toPlainText(StringBuilder builder)
{ {
builder.append( getKeybind() ); builder.append( getKeybind() );
super.toPlainText( builder ); super.toPlainText( builder );
} }
@Override @Override
protected void toLegacyText(StringVisitor builder) protected void toLegacyText(StringBuilder builder)
{ {
addFormat( builder ); addFormat( builder );
builder.append( getKeybind() ); builder.append( getKeybind() );

View File

@@ -85,14 +85,14 @@ public final class ScoreComponent extends BaseComponent
} }
@Override @Override
protected void toPlainText(StringVisitor builder) protected void toPlainText(StringBuilder builder)
{ {
builder.append( this.value ); builder.append( this.value );
super.toPlainText( builder ); super.toPlainText( builder );
} }
@Override @Override
protected void toLegacyText(StringVisitor builder) protected void toLegacyText(StringBuilder builder)
{ {
addFormat( builder ); addFormat( builder );
builder.append( this.value ); builder.append( this.value );

View File

@@ -69,14 +69,14 @@ public final class SelectorComponent extends BaseComponent
} }
@Override @Override
protected void toPlainText(StringVisitor builder) protected void toPlainText(StringBuilder builder)
{ {
builder.append( this.selector ); builder.append( this.selector );
super.toPlainText( builder ); super.toPlainText( builder );
} }
@Override @Override
protected void toLegacyText(StringVisitor builder) protected void toLegacyText(StringBuilder builder)
{ {
addFormat( builder ); addFormat( builder );
builder.append( this.selector ); builder.append( this.selector );

View File

@@ -280,14 +280,14 @@ public final class TextComponent extends BaseComponent
} }
@Override @Override
protected void toPlainText(StringVisitor builder) protected void toPlainText(StringBuilder builder)
{ {
builder.append( text ); builder.append( text );
super.toPlainText( builder ); super.toPlainText( builder );
} }
@Override @Override
protected void toLegacyText(StringVisitor builder) protected void toLegacyText(StringBuilder builder)
{ {
addFormat( builder ); addFormat( builder );
builder.append( text ); builder.append( text );

View File

@@ -19,7 +19,7 @@ import net.md_5.bungee.chat.TranslationRegistry;
public final class TranslatableComponent extends BaseComponent public final class TranslatableComponent extends BaseComponent
{ {
private static final Pattern FORMAT = Pattern.compile( "%(?:(\\d+)\\$)?([A-Za-z%]|$)" ); private final Pattern format = Pattern.compile( "%(?:(\\d+)\\$)?([A-Za-z%]|$)" );
/** /**
* The key into the Minecraft locale files to use for the translation. The * The key into the Minecraft locale files to use for the translation. The
@@ -44,11 +44,10 @@ public final class TranslatableComponent extends BaseComponent
{ {
super( original ); super( original );
setTranslate( original.getTranslate() ); setTranslate( original.getTranslate() );
setFallback( original.getFallback() );
if ( original.getWith() != null ) if ( original.getWith() != null )
{ {
List<BaseComponent> temp = new ArrayList<>(); List<BaseComponent> temp = new ArrayList<BaseComponent>();
for ( BaseComponent baseComponent : original.getWith() ) for ( BaseComponent baseComponent : original.getWith() )
{ {
temp.add( baseComponent.duplicate() ); temp.add( baseComponent.duplicate() );
@@ -156,20 +155,20 @@ public final class TranslatableComponent extends BaseComponent
} }
@Override @Override
protected void toPlainText(StringVisitor builder) protected void toPlainText(StringBuilder builder)
{ {
convert( builder, false ); convert( builder, false );
super.toPlainText( builder ); super.toPlainText( builder );
} }
@Override @Override
protected void toLegacyText(StringVisitor builder) protected void toLegacyText(StringBuilder builder)
{ {
convert( builder, true ); convert( builder, true );
super.toLegacyText( builder ); super.toLegacyText( builder );
} }
private void convert(StringVisitor builder, boolean applyFormat) private void convert(StringBuilder builder, boolean applyFormat)
{ {
String trans = TranslationRegistry.INSTANCE.translate( translate ); String trans = TranslationRegistry.INSTANCE.translate( translate );
@@ -178,7 +177,7 @@ public final class TranslatableComponent extends BaseComponent
trans = fallback; trans = fallback;
} }
Matcher matcher = FORMAT.matcher( trans ); Matcher matcher = format.matcher( trans );
int position = 0; int position = 0;
int i = 0; int i = 0;
while ( matcher.find( position ) ) while ( matcher.find( position ) )

View File

@@ -10,36 +10,27 @@ import com.google.gson.JsonSerializer;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.UUID; import java.util.UUID;
import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.chat.BaseComponentSerializer;
import net.md_5.bungee.chat.VersionedComponentSerializer;
public class EntitySerializer extends BaseComponentSerializer implements JsonSerializer<Entity>, JsonDeserializer<Entity> public class EntitySerializer implements JsonSerializer<Entity>, JsonDeserializer<Entity>
{ {
public EntitySerializer(VersionedComponentSerializer serializer)
{
super( serializer );
}
@Override @Override
public Entity deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException public Entity deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException
{ {
JsonObject value = element.getAsJsonObject(); JsonObject value = element.getAsJsonObject();
boolean newEntity = value.has( "uuid" );
String idString; String idString;
JsonElement uuid = value.get( newEntity ? "uuid" : "id" ); JsonElement id = value.get( "id" );
if ( uuid.isJsonArray() ) if ( id.isJsonArray() )
{ {
idString = parseUUID( context.deserialize( uuid, int[].class ) ).toString(); idString = parseUUID( context.deserialize( id, int[].class ) ).toString();
} else } else
{ {
idString = uuid.getAsString(); idString = id.getAsString();
} }
return new Entity( return new Entity(
( value.has( newEntity ? "id" : "type" ) ) ? value.get( newEntity ? "id" : "type" ).getAsString() : null, ( value.has( "type" ) ) ? value.get( "type" ).getAsString() : null,
idString, idString,
( value.has( "name" ) ) ? context.deserialize( value.get( "name" ), BaseComponent.class ) : null ( value.has( "name" ) ) ? context.deserialize( value.get( "name" ), BaseComponent.class ) : null
); );
@@ -49,21 +40,8 @@ public class EntitySerializer extends BaseComponentSerializer implements JsonSer
public JsonElement serialize(Entity content, Type type, JsonSerializationContext context) public JsonElement serialize(Entity content, Type type, JsonSerializationContext context)
{ {
JsonObject object = new JsonObject(); JsonObject object = new JsonObject();
switch ( serializer.getVersion() )
{
case V1_21_5:
object.addProperty( "id", ( content.getType() != null ) ? content.getType() : "minecraft:pig" );
object.addProperty( "uuid", content.getId() );
break;
case V1_16:
object.addProperty( "type", ( content.getType() != null ) ? content.getType() : "minecraft:pig" ); object.addProperty( "type", ( content.getType() != null ) ? content.getType() : "minecraft:pig" );
object.addProperty( "id", content.getId() ); object.addProperty( "id", content.getId() );
break;
default:
throw new IllegalArgumentException( "Unknown version " + serializer.getVersion() );
}
if ( content.getName() != null ) if ( content.getName() != null )
{ {
object.add( "name", context.serialize( content.getName() ) ); object.add( "name", context.serialize( content.getName() ) );

View File

@@ -0,0 +1,149 @@
package net.md_5.bungee.chat;
import com.google.common.base.Preconditions;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Locale;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentStyle;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.hover.content.Content;
public class BaseComponentSerializer
{
protected void deserialize(JsonObject object, BaseComponent component, JsonDeserializationContext context)
{
component.applyStyle( context.deserialize( object, ComponentStyle.class ) );
if ( object.has( "insertion" ) )
{
component.setInsertion( object.get( "insertion" ).getAsString() );
}
//Events
if ( object.has( "clickEvent" ) )
{
JsonObject event = object.getAsJsonObject( "clickEvent" );
component.setClickEvent( new ClickEvent(
ClickEvent.Action.valueOf( event.get( "action" ).getAsString().toUpperCase( Locale.ROOT ) ),
( event.has( "value" ) ) ? event.get( "value" ).getAsString() : "" ) );
}
if ( object.has( "hoverEvent" ) )
{
JsonObject event = object.getAsJsonObject( "hoverEvent" );
HoverEvent hoverEvent = null;
HoverEvent.Action action = HoverEvent.Action.valueOf( event.get( "action" ).getAsString().toUpperCase( Locale.ROOT ) );
if ( event.has( "value" ) )
{
JsonElement contents = event.get( "value" );
// Plugins previously had support to pass BaseComponent[] into any action.
// If the GSON is possible to be parsed as BaseComponent, attempt to parse as so.
BaseComponent[] components;
if ( contents.isJsonArray() )
{
components = context.deserialize( contents, BaseComponent[].class );
} else
{
components = new BaseComponent[]
{
context.deserialize( contents, BaseComponent.class )
};
}
hoverEvent = new HoverEvent( action, components );
} else if ( event.has( "contents" ) )
{
JsonElement contents = event.get( "contents" );
Content[] list;
if ( contents.isJsonArray() )
{
list = context.deserialize( contents, HoverEvent.getClass( action, true ) );
} else
{
list = new Content[]
{
context.deserialize( contents, HoverEvent.getClass( action, false ) )
};
}
hoverEvent = new HoverEvent( action, new ArrayList<>( Arrays.asList( list ) ) );
}
if ( hoverEvent != null )
{
component.setHoverEvent( hoverEvent );
}
}
if ( object.has( "extra" ) )
{
component.setExtra( Arrays.asList( context.<BaseComponent[]>deserialize( object.get( "extra" ), BaseComponent[].class ) ) );
}
}
protected void serialize(JsonObject object, BaseComponent component, JsonSerializationContext context)
{
boolean first = false;
if ( ComponentSerializer.serializedComponents.get() == null )
{
first = true;
ComponentSerializer.serializedComponents.set( Collections.newSetFromMap( new IdentityHashMap<BaseComponent, Boolean>() ) );
}
try
{
Preconditions.checkArgument( !ComponentSerializer.serializedComponents.get().contains( component ), "Component loop" );
ComponentSerializer.serializedComponents.get().add( component );
ComponentStyleSerializer.serializeTo( component.getStyle(), object );
if ( component.getInsertion() != null )
{
object.addProperty( "insertion", component.getInsertion() );
}
//Events
if ( component.getClickEvent() != null )
{
JsonObject clickEvent = new JsonObject();
clickEvent.addProperty( "action", component.getClickEvent().getAction().toString().toLowerCase( Locale.ROOT ) );
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( Locale.ROOT ) );
if ( component.getHoverEvent().isLegacy() )
{
hoverEvent.add( "value", context.serialize( component.getHoverEvent().getContents().get( 0 ) ) );
} else
{
hoverEvent.add( "contents", context.serialize( ( component.getHoverEvent().getContents().size() == 1 )
? component.getHoverEvent().getContents().get( 0 ) : component.getHoverEvent().getContents() ) );
}
object.add( "hoverEvent", hoverEvent );
}
if ( component.getExtra() != null )
{
object.add( "extra", context.serialize( component.getExtra() ) );
}
} finally
{
ComponentSerializer.serializedComponents.get().remove( component );
if ( first )
{
ComponentSerializer.serializedComponents.set( null );
}
}
}
}

View File

@@ -12,7 +12,6 @@ import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive; import com.google.gson.JsonPrimitive;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Set; import java.util.Set;
import lombok.Getter;
import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ComponentStyle; import net.md_5.bungee.api.chat.ComponentStyle;
import net.md_5.bungee.api.chat.ItemTag; import net.md_5.bungee.api.chat.ItemTag;
@@ -21,82 +20,30 @@ import net.md_5.bungee.api.chat.ScoreComponent;
import net.md_5.bungee.api.chat.SelectorComponent; import net.md_5.bungee.api.chat.SelectorComponent;
import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.chat.TranslatableComponent; import net.md_5.bungee.api.chat.TranslatableComponent;
import net.md_5.bungee.api.chat.hover.content.Content;
import net.md_5.bungee.api.chat.hover.content.Entity; import net.md_5.bungee.api.chat.hover.content.Entity;
import net.md_5.bungee.api.chat.hover.content.EntitySerializer; import net.md_5.bungee.api.chat.hover.content.EntitySerializer;
import net.md_5.bungee.api.chat.hover.content.Item; import net.md_5.bungee.api.chat.hover.content.Item;
import net.md_5.bungee.api.chat.hover.content.ItemSerializer; import net.md_5.bungee.api.chat.hover.content.ItemSerializer;
import net.md_5.bungee.api.chat.hover.content.Text; import net.md_5.bungee.api.chat.hover.content.Text;
import net.md_5.bungee.api.chat.hover.content.TextSerializer; import net.md_5.bungee.api.chat.hover.content.TextSerializer;
import net.md_5.bungee.api.dialog.Dialog;
import net.md_5.bungee.api.dialog.action.Action;
import net.md_5.bungee.api.dialog.chat.ShowDialogClickEvent;
import net.md_5.bungee.serializer.dialog.DialogActionSerializer;
import net.md_5.bungee.serializer.dialog.DialogSerializer;
import net.md_5.bungee.serializer.dialog.ShowDialogClickEventSerializer;
import org.jetbrains.annotations.ApiStatus;
@ApiStatus.Experimental public class ComponentSerializer implements JsonDeserializer<BaseComponent>
public class VersionedComponentSerializer implements JsonDeserializer<BaseComponent>
{ {
@Getter private static final Gson gson = new GsonBuilder().
@ApiStatus.Internal registerTypeAdapter( BaseComponent.class, new ComponentSerializer() ).
private final Gson gson; registerTypeAdapter( TextComponent.class, new TextComponentSerializer() ).
@Getter registerTypeAdapter( TranslatableComponent.class, new TranslatableComponentSerializer() ).
@ApiStatus.Internal registerTypeAdapter( KeybindComponent.class, new KeybindComponentSerializer() ).
private final ChatVersion version; registerTypeAdapter( ScoreComponent.class, new ScoreComponentSerializer() ).
@Getter registerTypeAdapter( SelectorComponent.class, new SelectorComponentSerializer() ).
@ApiStatus.Internal
private final DialogSerializer dialogSerializer;
public VersionedComponentSerializer(ChatVersion version)
{
this.version = version;
this.dialogSerializer = new DialogSerializer( this );
this.gson = new GsonBuilder().
registerTypeAdapter( BaseComponent.class, this ).
registerTypeAdapter( TextComponent.class, new TextComponentSerializer( this ) ).
registerTypeAdapter( TranslatableComponent.class, new TranslatableComponentSerializer( this ) ).
registerTypeAdapter( KeybindComponent.class, new KeybindComponentSerializer( this ) ).
registerTypeAdapter( ScoreComponent.class, new ScoreComponentSerializer( this ) ).
registerTypeAdapter( SelectorComponent.class, new SelectorComponentSerializer( this ) ).
registerTypeAdapter( ComponentStyle.class, new ComponentStyleSerializer() ). registerTypeAdapter( ComponentStyle.class, new ComponentStyleSerializer() ).
registerTypeAdapter( Entity.class, new EntitySerializer( this ) ). registerTypeAdapter( Entity.class, new EntitySerializer() ).
registerTypeAdapter( Text.class, new TextSerializer() ). registerTypeAdapter( Text.class, new TextSerializer() ).
registerTypeAdapter( Item.class, new ItemSerializer() ). registerTypeAdapter( Item.class, new ItemSerializer() ).
registerTypeAdapter( ItemTag.class, new ItemTag.Serializer() ). registerTypeAdapter( ItemTag.class, new ItemTag.Serializer() ).
// Dialogs
registerTypeAdapter( Dialog.class, dialogSerializer ).
registerTypeAdapter( ShowDialogClickEvent.class, new ShowDialogClickEventSerializer() ).
registerTypeAdapter( Action.class, new DialogActionSerializer() ).
create(); create();
}
private static final VersionedComponentSerializer v1_16 = new VersionedComponentSerializer( ChatVersion.V1_16 );
private static final VersionedComponentSerializer v1_21_5 = new VersionedComponentSerializer( ChatVersion.V1_21_5 );
public static VersionedComponentSerializer forVersion(ChatVersion version)
{
switch ( version )
{
case V1_16:
return v1_16;
case V1_21_5:
return v1_21_5;
default:
throw new IllegalArgumentException( "Unknown version " + version );
}
}
@Deprecated
@ApiStatus.Internal
public static VersionedComponentSerializer getDefault()
{
return v1_16;
}
@ApiStatus.Internal
public static final ThreadLocal<Set<BaseComponent>> serializedComponents = new ThreadLocal<Set<BaseComponent>>(); public static final ThreadLocal<Set<BaseComponent>> serializedComponents = new ThreadLocal<Set<BaseComponent>>();
/** /**
@@ -116,7 +63,7 @@ public class VersionedComponentSerializer implements JsonDeserializer<BaseCompon
* @param json the component json to parse * @param json the component json to parse
* @return an array of all parsed components * @return an array of all parsed components
*/ */
public BaseComponent[] parse(String json) public static BaseComponent[] parse(String json)
{ {
JsonElement jsonElement = JsonParser.parseString( json ); JsonElement jsonElement = JsonParser.parseString( json );
@@ -140,7 +87,7 @@ public class VersionedComponentSerializer implements JsonDeserializer<BaseCompon
* @throws IllegalArgumentException if anything other than a valid JSON * @throws IllegalArgumentException if anything other than a valid JSON
* component string is passed as input * component string is passed as input
*/ */
public BaseComponent deserialize(String json) public static BaseComponent deserialize(String json)
{ {
JsonElement jsonElement = JsonParser.parseString( json ); JsonElement jsonElement = JsonParser.parseString( json );
@@ -155,7 +102,7 @@ public class VersionedComponentSerializer implements JsonDeserializer<BaseCompon
* @throws IllegalArgumentException if anything other than a valid JSON * @throws IllegalArgumentException if anything other than a valid JSON
* component is passed as input * component is passed as input
*/ */
public BaseComponent deserialize(JsonElement jsonElement) public static BaseComponent deserialize(JsonElement jsonElement)
{ {
if ( jsonElement instanceof JsonPrimitive ) if ( jsonElement instanceof JsonPrimitive )
{ {
@@ -181,7 +128,7 @@ public class VersionedComponentSerializer implements JsonDeserializer<BaseCompon
* @throws IllegalArgumentException if anything other than a valid JSON * @throws IllegalArgumentException if anything other than a valid JSON
* component style string is passed as input * component style string is passed as input
*/ */
public ComponentStyle deserializeStyle(String json) public static ComponentStyle deserializeStyle(String json)
{ {
JsonElement jsonElement = JsonParser.parseString( json ); JsonElement jsonElement = JsonParser.parseString( json );
@@ -196,49 +143,32 @@ public class VersionedComponentSerializer implements JsonDeserializer<BaseCompon
* @throws IllegalArgumentException if anything other than a valid JSON * @throws IllegalArgumentException if anything other than a valid JSON
* component style is passed as input * component style is passed as input
*/ */
public ComponentStyle deserializeStyle(JsonElement jsonElement) public static ComponentStyle deserializeStyle(JsonElement jsonElement)
{ {
return gson.fromJson( jsonElement, ComponentStyle.class ); return gson.fromJson( jsonElement, ComponentStyle.class );
} }
public JsonElement toJson(BaseComponent component) public static JsonElement toJson(BaseComponent component)
{ {
return gson.toJsonTree( component ); return gson.toJsonTree( component );
} }
public JsonElement toJson(ComponentStyle style) public static JsonElement toJson(ComponentStyle style)
{ {
return gson.toJsonTree( style ); return gson.toJsonTree( style );
} }
/** public static String toString(Object object)
* @param object the object to serialize
* @return the JSON string representation of the object
* @deprecated Error-prone, be careful which object you input here
*/
@Deprecated
public String toString(Object object)
{ {
return gson.toJson( object ); return gson.toJson( object );
} }
/** public static String toString(BaseComponent component)
* @param content the content to serialize
* @return the JSON string representation of the object
* @deprecated for legacy internal use only
*/
@Deprecated
public String toString(Content content)
{
return gson.toJson( content );
}
public String toString(BaseComponent component)
{ {
return gson.toJson( component ); return gson.toJson( component );
} }
public String toString(BaseComponent... components) public static String toString(BaseComponent... components)
{ {
if ( components.length == 1 ) if ( components.length == 1 )
{ {
@@ -249,7 +179,7 @@ public class VersionedComponentSerializer implements JsonDeserializer<BaseCompon
} }
} }
public String toString(ComponentStyle style) public static String toString(ComponentStyle style)
{ {
return gson.toJson( style ); return gson.toJson( style );
} }
@@ -261,18 +191,6 @@ public class VersionedComponentSerializer implements JsonDeserializer<BaseCompon
{ {
return new TextComponent( json.getAsString() ); return new TextComponent( json.getAsString() );
} }
if ( json.isJsonArray() )
{
JsonArray arr = json.getAsJsonArray();
BaseComponent[] components = new BaseComponent[arr.size()];
for ( int i = 0; i < arr.size(); i++ )
{
components[i] = deserialize( arr.get( i ), BaseComponent.class, context );
}
return TextComponent.fromArray( components );
}
JsonObject object = json.getAsJsonObject(); JsonObject object = json.getAsJsonObject();
if ( object.has( "translate" ) ) if ( object.has( "translate" ) )
{ {

View File

@@ -1,6 +1,5 @@
package net.md_5.bungee.chat; package net.md_5.bungee.chat;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer; import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
@@ -9,9 +8,7 @@ import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive; import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer; import com.google.gson.JsonSerializer;
import java.awt.Color;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Map;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.ComponentStyle; import net.md_5.bungee.api.chat.ComponentStyle;
import net.md_5.bungee.api.chat.ComponentStyleBuilder; import net.md_5.bungee.api.chat.ComponentStyleBuilder;
@@ -65,14 +62,10 @@ public class ComponentStyleSerializer implements JsonSerializer<ComponentStyle>,
{ {
object.addProperty( "obfuscated", style.isObfuscatedRaw() ); object.addProperty( "obfuscated", style.isObfuscatedRaw() );
} }
if ( style.hasColor() && style.getColor().getColor() != null ) if ( style.hasColor() )
{ {
object.addProperty( "color", style.getColor().getName() ); object.addProperty( "color", style.getColor().getName() );
} }
if ( style.hasShadowColor() )
{
object.addProperty( "shadow_color", style.getShadowColor().getRGB() );
}
if ( style.hasFont() ) if ( style.hasFont() )
{ {
object.addProperty( "font", style.getFont() ); object.addProperty( "font", style.getFont() );
@@ -84,45 +77,33 @@ public class ComponentStyleSerializer implements JsonSerializer<ComponentStyle>,
{ {
ComponentStyleBuilder builder = ComponentStyle.builder(); ComponentStyleBuilder builder = ComponentStyle.builder();
JsonObject object = json.getAsJsonObject(); JsonObject object = json.getAsJsonObject();
for ( Map.Entry<String, JsonElement> entry : object.entrySet() ) if ( object.has( "bold" ) )
{ {
String name = entry.getKey(); builder.bold( getAsBoolean( object.get( "bold" ) ) );
JsonElement value = entry.getValue();
switch ( name )
{
case "bold":
builder.bold( getAsBoolean( value ) );
break;
case "italic":
builder.italic( getAsBoolean( value ) );
break;
case "underlined":
builder.underlined( getAsBoolean( value ) );
break;
case "strikethrough":
builder.strikethrough( getAsBoolean( value ) );
break;
case "obfuscated":
builder.obfuscated( getAsBoolean( value ) );
break;
case "color":
builder.color( ChatColor.of( value.getAsString() ) );
break;
case "shadow_color":
if ( value.isJsonArray() )
{
JsonArray array = value.getAsJsonArray();
builder.shadowColor( new Color( array.get( 0 ).getAsFloat(), array.get( 1 ).getAsFloat(), array.get( 2 ).getAsFloat(), array.get( 3 ).getAsFloat() ) );
} else if ( value.isJsonPrimitive() )
{
builder.shadowColor( new Color( value.getAsNumber().intValue(), true ) );
} }
break; if ( object.has( "italic" ) )
case "font": {
builder.font( value.getAsString() ); builder.italic( getAsBoolean( object.get( "italic" ) ) );
break;
} }
if ( object.has( "underlined" ) )
{
builder.underlined( getAsBoolean( object.get( "underlined" ) ) );
}
if ( object.has( "strikethrough" ) )
{
builder.strikethrough( getAsBoolean( object.get( "strikethrough" ) ) );
}
if ( object.has( "obfuscated" ) )
{
builder.obfuscated( getAsBoolean( object.get( "obfuscated" ) ) );
}
if ( object.has( "color" ) )
{
builder.color( ChatColor.of( object.get( "color" ).getAsString() ) );
}
if ( object.has( "font" ) )
{
builder.font( object.get( "font" ).getAsString() );
} }
return builder.build(); return builder.build();
} }

View File

@@ -13,23 +13,17 @@ import net.md_5.bungee.api.chat.KeybindComponent;
public class KeybindComponentSerializer extends BaseComponentSerializer implements JsonSerializer<KeybindComponent>, JsonDeserializer<KeybindComponent> public class KeybindComponentSerializer extends BaseComponentSerializer implements JsonSerializer<KeybindComponent>, JsonDeserializer<KeybindComponent>
{ {
public KeybindComponentSerializer(VersionedComponentSerializer serializer)
{
super( serializer );
}
@Override @Override
public KeybindComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException public KeybindComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{ {
JsonObject object = json.getAsJsonObject(); JsonObject object = json.getAsJsonObject();
JsonElement keybind = object.get( "keybind" ); if ( !object.has( "keybind" ) )
if ( keybind == null )
{ {
throw new JsonParseException( "Could not parse JSON: missing 'keybind' property" ); throw new JsonParseException( "Could not parse JSON: missing 'keybind' property" );
} }
KeybindComponent component = new KeybindComponent(); KeybindComponent component = new KeybindComponent();
deserialize( object, component, context ); deserialize( object, component, context );
component.setKeybind( keybind.getAsString() ); component.setKeybind( object.get( "keybind" ).getAsString() );
return component; return component;
} }

View File

@@ -13,38 +13,26 @@ import net.md_5.bungee.api.chat.ScoreComponent;
public class ScoreComponentSerializer extends BaseComponentSerializer implements JsonSerializer<ScoreComponent>, JsonDeserializer<ScoreComponent> public class ScoreComponentSerializer extends BaseComponentSerializer implements JsonSerializer<ScoreComponent>, JsonDeserializer<ScoreComponent>
{ {
public ScoreComponentSerializer(VersionedComponentSerializer serializer)
{
super( serializer );
}
@Override @Override
public ScoreComponent deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException public ScoreComponent deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException
{ {
JsonObject json = element.getAsJsonObject(); JsonObject json = element.getAsJsonObject();
JsonObject score = json.getAsJsonObject( "score" ); if ( !json.has( "score" ) )
if ( score == null )
{ {
throw new JsonParseException( "Could not parse JSON: missing 'score' property" ); throw new JsonParseException( "Could not parse JSON: missing 'score' property" );
} }
JsonElement nameJson = score.get( "name" ); JsonObject score = json.get( "score" ).getAsJsonObject();
if ( nameJson == null ) if ( !score.has( "name" ) || !score.has( "objective" ) )
{
throw new JsonParseException( "A score component needs at least a name (and an objective)" );
}
JsonElement objectiveJson = score.get( "objective" );
if ( objectiveJson == null )
{ {
throw new JsonParseException( "A score component needs at least a name and an objective" ); throw new JsonParseException( "A score component needs at least a name and an objective" );
} }
String name = nameJson.getAsString(); String name = score.get( "name" ).getAsString();
String objective = objectiveJson.getAsString(); String objective = score.get( "objective" ).getAsString();
ScoreComponent component = new ScoreComponent( name, objective ); ScoreComponent component = new ScoreComponent( name, objective );
JsonElement value = score.get( "value" ); if ( score.has( "value" ) && !score.get( "value" ).getAsString().isEmpty() )
if ( value != null && !value.getAsString().isEmpty() )
{ {
component.setValue( value.getAsString() ); component.setValue( score.get( "value" ).getAsString() );
} }
deserialize( json, component, context ); deserialize( json, component, context );

View File

@@ -13,26 +13,19 @@ import net.md_5.bungee.api.chat.SelectorComponent;
public class SelectorComponentSerializer extends BaseComponentSerializer implements JsonSerializer<SelectorComponent>, JsonDeserializer<SelectorComponent> public class SelectorComponentSerializer extends BaseComponentSerializer implements JsonSerializer<SelectorComponent>, JsonDeserializer<SelectorComponent>
{ {
public SelectorComponentSerializer(VersionedComponentSerializer serializer)
{
super( serializer );
}
@Override @Override
public SelectorComponent deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException public SelectorComponent deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException
{ {
JsonObject object = element.getAsJsonObject(); JsonObject object = element.getAsJsonObject();
JsonElement selector = object.get( "selector" ); if ( !object.has( "selector" ) )
if ( selector == null )
{ {
throw new JsonParseException( "Could not parse JSON: missing 'selector' property" ); throw new JsonParseException( "Could not parse JSON: missing 'selector' property" );
} }
SelectorComponent component = new SelectorComponent( selector.getAsString() ); SelectorComponent component = new SelectorComponent( object.get( "selector" ).getAsString() );
JsonElement separator = object.get( "separator" ); if ( object.has( "separator" ) )
if ( separator != null )
{ {
component.setSeparator( serializer.deserialize( separator.getAsString() ) ); component.setSeparator( ComponentSerializer.deserialize( object.get( "separator" ).getAsString() ) );
} }
deserialize( object, component, context ); deserialize( object, component, context );
@@ -48,7 +41,7 @@ public class SelectorComponentSerializer extends BaseComponentSerializer impleme
if ( component.getSeparator() != null ) if ( component.getSeparator() != null )
{ {
object.addProperty( "separator", serializer.toString( component.getSeparator() ) ); object.addProperty( "separator", ComponentSerializer.toString( component.getSeparator() ) );
} }
return object; return object;
} }

View File

@@ -13,20 +13,14 @@ import net.md_5.bungee.api.chat.TextComponent;
public class TextComponentSerializer extends BaseComponentSerializer implements JsonSerializer<TextComponent>, JsonDeserializer<TextComponent> public class TextComponentSerializer extends BaseComponentSerializer implements JsonSerializer<TextComponent>, JsonDeserializer<TextComponent>
{ {
public TextComponentSerializer(VersionedComponentSerializer serializer)
{
super( serializer );
}
@Override @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(); TextComponent component = new TextComponent();
JsonObject object = json.getAsJsonObject(); JsonObject object = json.getAsJsonObject();
JsonElement text = object.get( "text" ); if ( object.has( "text" ) )
if ( text != null )
{ {
component.setText( text.getAsString() ); component.setText( object.get( "text" ).getAsString() );
} }
deserialize( object, component, context ); deserialize( object, component, context );
return component; return component;

View File

@@ -15,32 +15,24 @@ import net.md_5.bungee.api.chat.TranslatableComponent;
public class TranslatableComponentSerializer extends BaseComponentSerializer implements JsonSerializer<TranslatableComponent>, JsonDeserializer<TranslatableComponent> public class TranslatableComponentSerializer extends BaseComponentSerializer implements JsonSerializer<TranslatableComponent>, JsonDeserializer<TranslatableComponent>
{ {
public TranslatableComponentSerializer(VersionedComponentSerializer serializer)
{
super( serializer );
}
@Override @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(); TranslatableComponent component = new TranslatableComponent();
JsonObject object = json.getAsJsonObject(); JsonObject object = json.getAsJsonObject();
deserialize( object, component, context ); deserialize( object, component, context );
JsonElement translate = object.get( "translate" ); if ( !object.has( "translate" ) )
if ( translate == null )
{ {
throw new JsonParseException( "Could not parse JSON: missing 'translate' property" ); throw new JsonParseException( "Could not parse JSON: missing 'translate' property" );
} }
component.setTranslate( translate.getAsString() ); component.setTranslate( object.get( "translate" ).getAsString() );
JsonElement with = object.get( "with" ); if ( object.has( "with" ) )
if ( with != null )
{ {
component.setWith( Arrays.asList( context.deserialize( with, BaseComponent[].class ) ) ); component.setWith( Arrays.asList( context.deserialize( object.get( "with" ), BaseComponent[].class ) ) );
} }
JsonElement fallback = object.get( "fallback" ); if ( object.has( "fallback" ) )
if ( fallback != null )
{ {
component.setFallback( fallback.getAsString() ); component.setFallback( object.get( "fallback" ).getAsString() );
} }
return component; return component;
} }

View File

@@ -1,6 +1,5 @@
package net.md_5.bungee.api.chat; package net.md_5.bungee.api.chat;
import static net.md_5.bungee.api.ChatColor.*;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
import java.awt.Color; import java.awt.Color;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@@ -8,6 +7,7 @@ import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.ObjIntConsumer; import java.util.function.ObjIntConsumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.hover.content.Entity; import net.md_5.bungee.api.chat.hover.content.Entity;
import net.md_5.bungee.api.chat.hover.content.Text; import net.md_5.bungee.api.chat.hover.content.Text;
import net.md_5.bungee.chat.ComponentSerializer; import net.md_5.bungee.chat.ComponentSerializer;
@@ -20,14 +20,14 @@ public class ComponentsTest
{ {
String json = ComponentSerializer.toString( components ); String json = ComponentSerializer.toString( components );
BaseComponent[] parsed = ComponentSerializer.parse( json ); BaseComponent[] parsed = ComponentSerializer.parse( json );
assertEquals( BaseComponent.toLegacyText( parsed ), BaseComponent.toLegacyText( components ) ); assertEquals( TextComponent.toLegacyText( parsed ), TextComponent.toLegacyText( components ) );
} }
public static void testDissembleReassemble(BaseComponent component) public static void testDissembleReassemble(BaseComponent component)
{ {
String json = ComponentSerializer.toString( component ); String json = ComponentSerializer.toString( component );
BaseComponent[] parsed = ComponentSerializer.parse( json ); BaseComponent[] parsed = ComponentSerializer.parse( json );
assertEquals( BaseComponent.toLegacyText( parsed ), BaseComponent.toLegacyText( component ) ); assertEquals( TextComponent.toLegacyText( parsed ), TextComponent.toLegacyText( component ) );
} }
public static void testAssembleDissemble(String json, boolean modern) public static void testAssembleDissemble(String json, boolean modern)
@@ -100,7 +100,7 @@ public class ComponentsTest
@Test @Test
public void testEmptyComponentBuilderCreate() public void testEmptyComponentBuilderCreate()
{ {
testEmptyComponentBuilder( this.testEmptyComponentBuilder(
ComponentBuilder::create, ComponentBuilder::create,
(components) -> assertEquals( components.length, 0 ), (components) -> assertEquals( components.length, 0 ),
(components, size) -> assertEquals( size, components.length ) (components, size) -> assertEquals( size, components.length )
@@ -110,14 +110,14 @@ public class ComponentsTest
@Test @Test
public void testEmptyComponentBuilderBuild() public void testEmptyComponentBuilderBuild()
{ {
testEmptyComponentBuilder( this.testEmptyComponentBuilder(
ComponentBuilder::build, ComponentBuilder::build,
(component) -> assertNull( component.getExtra() ), (component) -> assertNull( component.getExtra() ),
(component, size) -> assertEquals( component.getExtra().size(), size ) (component, size) -> assertEquals( component.getExtra().size(), size )
); );
} }
private static <T> void testEmptyComponentBuilder(Function<ComponentBuilder, T> componentBuilder, Consumer<T> emptyAssertion, ObjIntConsumer<T> sizedAssertion) private <T> void testEmptyComponentBuilder(Function<ComponentBuilder, T> componentBuilder, Consumer<T> emptyAssertion, ObjIntConsumer<T> sizedAssertion)
{ {
ComponentBuilder builder = new ComponentBuilder(); ComponentBuilder builder = new ComponentBuilder();
@@ -137,9 +137,9 @@ public class ComponentsTest
{ {
ComponentBuilder builder = new ComponentBuilder(); ComponentBuilder builder = new ComponentBuilder();
assertNotNull( builder.getCurrentComponent() ); assertNotNull( builder.getCurrentComponent() );
builder.color( GREEN ); builder.color( ChatColor.GREEN );
builder.append( "test ", ComponentBuilder.FormatRetention.ALL ); builder.append( "test ", ComponentBuilder.FormatRetention.ALL );
assertEquals( builder.getCurrentComponent().getColor(), GREEN ); assertEquals( builder.getCurrentComponent().getColor(), ChatColor.GREEN );
} }
@Test @Test
@@ -155,17 +155,6 @@ public class ComponentsTest
assertThrows( IndexOutOfBoundsException.class, () -> builder.getComponent( 1 ) ); assertThrows( IndexOutOfBoundsException.class, () -> builder.getComponent( 1 ) );
} }
@Test
public void testFormatNotColor()
{
BaseComponent[] component = new ComponentBuilder().color( BOLD ).append( "Test" ).create();
String json = ComponentSerializer.toString( component );
BaseComponent[] parsed = ComponentSerializer.parse( json );
assertNull( parsed[0].getColorRaw(), "Format should not be preserved as color" );
}
@Test @Test
public void testComponentParting() public void testComponentParting()
{ {
@@ -187,8 +176,8 @@ public class ComponentsTest
@Test @Test
public void testToLegacyFromLegacy() public void testToLegacyFromLegacy()
{ {
String text = "" + GREEN + BOLD + "Hello " + WHITE + MAGIC + "world" + GRAY + "!"; String text = "§a§lHello §f§kworld§7!";
assertEquals( text, BaseComponent.toLegacyText( TextComponent.fromLegacyText( text ) ) ); assertEquals( text, TextComponent.toLegacyText( TextComponent.fromLegacyText( text ) ) );
} }
@Test @Test
@@ -229,7 +218,7 @@ public class ComponentsTest
@Test @Test
public void testLegacyComponentBuilderAppend() public void testLegacyComponentBuilderAppend()
{ {
String text = "" + GREEN + BOLD + "Hello " + RESET + MAGIC + "world" + GRAY + "!"; String text = "§a§lHello §r§kworld§7!";
BaseComponent[] components = TextComponent.fromLegacyText( text ); BaseComponent[] components = TextComponent.fromLegacyText( text );
BaseComponent[] builderComponents = new ComponentBuilder().append( components ).create(); BaseComponent[] builderComponents = new ComponentBuilder().append( components ).create();
assertArrayEquals( components, builderComponents ); assertArrayEquals( components, builderComponents );
@@ -348,20 +337,20 @@ public class ComponentsTest
@Test @Test
public void testFormatRetentionCopyFormattingCreate() public void testFormatRetentionCopyFormattingCreate()
{ {
testFormatRetentionCopyFormatting( () -> new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Test" ).create() ) ); this.testFormatRetentionCopyFormatting( () -> new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Test" ).create() ) );
} }
@Test @Test
public void testFormatRetentionCopyFormattingBuild() public void testFormatRetentionCopyFormattingBuild()
{ {
testFormatRetentionCopyFormatting( () -> new HoverEvent( HoverEvent.Action.SHOW_TEXT, new Text( new ComponentBuilder( "Test" ).build() ) ) ); this.testFormatRetentionCopyFormatting( () -> new HoverEvent( HoverEvent.Action.SHOW_TEXT, new Text( new ComponentBuilder( "Test" ).build() ) ) );
} }
private static void testFormatRetentionCopyFormatting(Supplier<HoverEvent> hoverEventSupplier) private void testFormatRetentionCopyFormatting(Supplier<HoverEvent> hoverEventSupplier)
{ {
TextComponent first = new TextComponent( "Hello" ); TextComponent first = new TextComponent( "Hello" );
first.setBold( true ); first.setBold( true );
first.setColor( RED ); first.setColor( ChatColor.RED );
first.setClickEvent( new ClickEvent( ClickEvent.Action.RUN_COMMAND, "test" ) ); first.setClickEvent( new ClickEvent( ClickEvent.Action.RUN_COMMAND, "test" ) );
first.setHoverEvent( hoverEventSupplier.get() ); first.setHoverEvent( hoverEventSupplier.get() );
@@ -376,18 +365,18 @@ public class ComponentsTest
@Test @Test
public void testBuilderCloneCreate() public void testBuilderCloneCreate()
{ {
testBuilderClone( (builder) -> BaseComponent.toLegacyText( builder.create() ) ); this.testBuilderClone( (builder) -> TextComponent.toLegacyText( builder.create() ) );
} }
@Test @Test
public void testBuilderCloneBuild() public void testBuilderCloneBuild()
{ {
testBuilderClone( (builder) -> BaseComponent.toLegacyText( builder.build() ) ); this.testBuilderClone( (builder) -> TextComponent.toLegacyText( builder.build() ) );
} }
private static void testBuilderClone(Function<ComponentBuilder, String> legacyTextFunction) private void testBuilderClone(Function<ComponentBuilder, String> legacyTextFunction)
{ {
ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( RED ).append( "world" ).color( DARK_RED ); ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( ChatColor.RED ).append( "world" ).color( ChatColor.DARK_RED );
ComponentBuilder cloned = new ComponentBuilder( builder ); ComponentBuilder cloned = new ComponentBuilder( builder );
assertEquals( legacyTextFunction.apply( builder ), legacyTextFunction.apply( cloned ) ); assertEquals( legacyTextFunction.apply( builder ), legacyTextFunction.apply( cloned ) );
@@ -396,7 +385,7 @@ public class ComponentsTest
@Test @Test
public void testBuilderAppendCreateMixedComponents() public void testBuilderAppendCreateMixedComponents()
{ {
testBuilderAppendMixedComponents( this.testBuilderAppendMixedComponents(
ComponentBuilder::create, ComponentBuilder::create,
(components, index) -> components[index] (components, index) -> components[index]
); );
@@ -405,13 +394,13 @@ public class ComponentsTest
@Test @Test
public void testBuilderAppendBuildMixedComponents() public void testBuilderAppendBuildMixedComponents()
{ {
testBuilderAppendMixedComponents( this.testBuilderAppendMixedComponents(
ComponentBuilder::build, ComponentBuilder::build,
(component, index) -> component.getExtra().get( index ) (component, index) -> component.getExtra().get( index )
); );
} }
private static <T> void testBuilderAppendMixedComponents(Function<ComponentBuilder, T> componentBuilder, BiFunction<T, Integer, BaseComponent> extraGetter) private <T> void testBuilderAppendMixedComponents(Function<ComponentBuilder, T> componentBuilder, BiFunction<T, Integer, BaseComponent> extraGetter)
{ {
ComponentBuilder builder = new ComponentBuilder( "Hello " ); ComponentBuilder builder = new ComponentBuilder( "Hello " );
TextComponent textComponent = new TextComponent( "world " ); TextComponent textComponent = new TextComponent( "world " );
@@ -454,12 +443,12 @@ public class ComponentsTest
@Test @Test
public void testBuilderAppendCreate() public void testBuilderAppendCreate()
{ {
testBuilderAppend( this.testBuilderAppend(
() -> new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Hello world" ).create() ), () -> new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Hello world" ).create() ),
ComponentBuilder::create, ComponentBuilder::create,
(components, index) -> components[index], (components, index) -> components[index],
BaseComponent::toPlainText, BaseComponent::toPlainText,
YELLOW + "Hello " + GREEN + "world!", ChatColor.YELLOW + "Hello " + ChatColor.GREEN + "world!",
BaseComponent::toLegacyText BaseComponent::toLegacyText
); );
} }
@@ -467,24 +456,24 @@ public class ComponentsTest
@Test @Test
public void testBuilderAppendBuild() public void testBuilderAppendBuild()
{ {
testBuilderAppend( this.testBuilderAppend(
() -> new HoverEvent( HoverEvent.Action.SHOW_TEXT, new Text( new ComponentBuilder( "Hello world" ).build() ) ), () -> new HoverEvent( HoverEvent.Action.SHOW_TEXT, new Text( new ComponentBuilder( "Hello world" ).build() ) ),
ComponentBuilder::build, ComponentBuilder::build,
(component, index) -> component.getExtra().get( index ), (component, index) -> component.getExtra().get( index ),
(component) -> BaseComponent.toPlainText( component ), (component) -> BaseComponent.toPlainText( component ),
// An extra format code is appended to the beginning because there is an empty TextComponent at the start of every component // An extra format code is appended to the beginning because there is an empty TextComponent at the start of every component
WHITE.toString() + YELLOW + "Hello " + GREEN + "world!", ChatColor.WHITE.toString() + ChatColor.YELLOW + "Hello " + ChatColor.GREEN + "world!",
(component) -> BaseComponent.toLegacyText( component ) (component) -> BaseComponent.toLegacyText( component )
); );
} }
private static <T> void testBuilderAppend(Supplier<HoverEvent> hoverEventSupplier, Function<ComponentBuilder, T> componentBuilder, BiFunction<T, Integer, BaseComponent> extraGetter, Function<T, String> toPlainTextFunction, String expectedLegacyText, Function<T, String> toLegacyTextFunction) private <T> void testBuilderAppend(Supplier<HoverEvent> hoverEventSupplier, Function<ComponentBuilder, T> componentBuilder, BiFunction<T, Integer, BaseComponent> extraGetter, Function<T, String> toPlainTextFunction, String expectedLegacyText, Function<T, String> toLegacyTextFunction)
{ {
ClickEvent clickEvent = new ClickEvent( ClickEvent.Action.RUN_COMMAND, "/help " ); ClickEvent clickEvent = new ClickEvent( ClickEvent.Action.RUN_COMMAND, "/help " );
HoverEvent hoverEvent = hoverEventSupplier.get(); HoverEvent hoverEvent = hoverEventSupplier.get();
ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( YELLOW ); ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( ChatColor.YELLOW );
builder.append( new ComponentBuilder( "world!" ).color( GREEN ).event( hoverEvent ).event( clickEvent ).create() ); // Intentionally using create() to append multiple individual components builder.append( new ComponentBuilder( "world!" ).color( ChatColor.GREEN ).event( hoverEvent ).event( clickEvent ).create() ); // Intentionally using create() to append multiple individual components
T component = componentBuilder.apply( builder ); T component = componentBuilder.apply( builder );
@@ -497,10 +486,10 @@ public class ComponentsTest
@Test @Test
public void testBuilderAppendLegacyCreate() public void testBuilderAppendLegacyCreate()
{ {
testBuilderAppendLegacy( this.testBuilderAppendLegacy(
ComponentBuilder::create, ComponentBuilder::create,
BaseComponent::toPlainText, BaseComponent::toPlainText,
YELLOW + "Hello " + GREEN + "world!", ChatColor.YELLOW + "Hello " + ChatColor.GREEN + "world!",
BaseComponent::toLegacyText BaseComponent::toLegacyText
); );
} }
@@ -508,19 +497,19 @@ public class ComponentsTest
@Test @Test
public void testBuilderAppendLegacyBuild() public void testBuilderAppendLegacyBuild()
{ {
testBuilderAppendLegacy( this.testBuilderAppendLegacy(
ComponentBuilder::build, ComponentBuilder::build,
(component) -> BaseComponent.toPlainText( component ), (component) -> BaseComponent.toPlainText( component ),
// An extra format code is appended to the beginning because there is an empty TextComponent at the start of every component // An extra format code is appended to the beginning because there is an empty TextComponent at the start of every component
WHITE.toString() + YELLOW + "Hello " + GREEN + "world!", ChatColor.WHITE.toString() + ChatColor.YELLOW + "Hello " + ChatColor.GREEN + "world!",
(component) -> BaseComponent.toLegacyText( component ) (component) -> BaseComponent.toLegacyText( component )
); );
} }
private static <T> void testBuilderAppendLegacy(Function<ComponentBuilder, T> componentBuilder, Function<T, String> toPlainTextFunction, String expectedLegacyString, Function<T, String> toLegacyTextFunction) private <T> void testBuilderAppendLegacy(Function<ComponentBuilder, T> componentBuilder, Function<T, String> toPlainTextFunction, String expectedLegacyString, Function<T, String> toLegacyTextFunction)
{ {
ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( YELLOW ); ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( ChatColor.YELLOW );
builder.appendLegacy( GREEN + "world!" ); builder.appendLegacy( "§aworld!" );
T component = componentBuilder.apply( builder ); T component = componentBuilder.apply( builder );
@@ -532,26 +521,26 @@ public class ComponentsTest
public void testBasicComponent() public void testBasicComponent()
{ {
TextComponent textComponent = new TextComponent( "Hello world" ); TextComponent textComponent = new TextComponent( "Hello world" );
textComponent.setColor( RED ); textComponent.setColor( ChatColor.RED );
assertEquals( "Hello world", textComponent.toPlainText() ); assertEquals( "Hello world", textComponent.toPlainText() );
assertEquals( RED + "Hello world", textComponent.toLegacyText() ); assertEquals( ChatColor.RED + "Hello world", textComponent.toLegacyText() );
} }
@Test @Test
public void testLegacyConverter() public void testLegacyConverter()
{ {
BaseComponent[] test1 = TextComponent.fromLegacyText( AQUA + "Aqua " + RED + BOLD + "RedBold" ); BaseComponent[] test1 = TextComponent.fromLegacyText( ChatColor.AQUA + "Aqua " + ChatColor.RED + ChatColor.BOLD + "RedBold" );
assertEquals( "Aqua RedBold", BaseComponent.toPlainText( test1 ) ); assertEquals( "Aqua RedBold", BaseComponent.toPlainText( test1 ) );
assertEquals( AQUA + "Aqua " + RED + BOLD + "RedBold", BaseComponent.toLegacyText( test1 ) ); assertEquals( ChatColor.AQUA + "Aqua " + ChatColor.RED + ChatColor.BOLD + "RedBold", BaseComponent.toLegacyText( test1 ) );
BaseComponent[] test2 = TextComponent.fromLegacyText( "Text http://spigotmc.org " + GREEN + "google.com/test" ); BaseComponent[] test2 = TextComponent.fromLegacyText( "Text http://spigotmc.org " + ChatColor.GREEN + "google.com/test" );
assertEquals( "Text http://spigotmc.org google.com/test", BaseComponent.toPlainText( test2 ) ); assertEquals( "Text http://spigotmc.org google.com/test", BaseComponent.toPlainText( test2 ) );
//The extra ChatColor instances are sometimes inserted when not needed but it doesn't change the result //The extra ChatColor instances are sometimes inserted when not needed but it doesn't change the result
assertEquals( WHITE + "Text " + WHITE + "http://spigotmc.org" + WHITE assertEquals( ChatColor.WHITE + "Text " + ChatColor.WHITE + "http://spigotmc.org" + ChatColor.WHITE
+ " " + GREEN + "google.com/test" + GREEN, BaseComponent.toLegacyText( test2 ) ); + " " + ChatColor.GREEN + "google.com/test" + ChatColor.GREEN, BaseComponent.toLegacyText( test2 ) );
ClickEvent url1 = test2[1].getClickEvent(); ClickEvent url1 = test2[1].getClickEvent();
assertNotNull( url1 ); assertNotNull( url1 );
@@ -565,21 +554,35 @@ public class ComponentsTest
} }
@Test @Test
public void testLong() public void testTranslateComponent()
{ {
BaseComponent test = ComponentSerializer.deserialize( "{\"translate\":\"%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s\",\"with\":[{\"translate\":\"%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s\",\"with\":[{\"translate\":\"%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s\",\"with\":[{\"translate\":\"%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s\",\"with\":[{\"translate\":\"%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s\",\"with\":[{\"translate\":\"%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s\",\"with\":[{\"translate\":\"%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s\",\"with\":[{\"translate\":\"%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s\",\"with\":[{\"translate\":\"%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s\",\"with\":[{\"translate\":\"%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s\",\"with\":[\"Test\"]}]}]}]}]}]}]}]}]}]}" ); TranslatableComponent item = new TranslatableComponent( "item.swordGold.name" );
item.setColor( ChatColor.AQUA );
TranslatableComponent translatableComponent = new TranslatableComponent( "commands.give.success",
item, "5",
"thinkofdeath" );
assertThrows( RuntimeException.class, test::toLegacyText ); assertEquals( "Given Golden Sword * 5 to thinkofdeath", translatableComponent.toPlainText() );
assertThrows( RuntimeException.class, test::toPlainText ); assertEquals( ChatColor.WHITE + "Given " + ChatColor.AQUA + "Golden Sword" + ChatColor.WHITE
+ " * " + ChatColor.WHITE + "5" + ChatColor.WHITE + " to " + ChatColor.WHITE + "thinkofdeath",
translatableComponent.toLegacyText() );
TranslatableComponent positional = new TranslatableComponent( "book.pageIndicator", "5", "50" );
assertEquals( "Page 5 of 50", positional.toPlainText() );
assertEquals( ChatColor.WHITE + "Page " + ChatColor.WHITE + "5" + ChatColor.WHITE + " of " + ChatColor.WHITE + "50", positional.toLegacyText() );
TranslatableComponent one_four_two = new TranslatableComponent( "filled_map.buried_treasure" );
assertEquals( "Buried Treasure Map", one_four_two.toPlainText() );
} }
@Test @Test
public void testBuilderCreate() public void testBuilderCreate()
{ {
testBuilder( this.testBuilder(
ComponentBuilder::create, ComponentBuilder::create,
BaseComponent::toPlainText, BaseComponent::toPlainText,
RED + "Hello " + BLUE + BOLD + "World" + YELLOW + BOLD + "!", ChatColor.RED + "Hello " + ChatColor.BLUE + ChatColor.BOLD + "World" + ChatColor.YELLOW + ChatColor.BOLD + "!",
BaseComponent::toLegacyText BaseComponent::toLegacyText
); );
} }
@@ -587,20 +590,20 @@ public class ComponentsTest
@Test @Test
public void testBuilderBuild() public void testBuilderBuild()
{ {
testBuilder( this.testBuilder(
ComponentBuilder::build, ComponentBuilder::build,
(component) -> BaseComponent.toPlainText( component ), (component) -> BaseComponent.toPlainText( component ),
// An extra format code is appended to the beginning because there is an empty TextComponent at the start of every component // An extra format code is appended to the beginning because there is an empty TextComponent at the start of every component
WHITE.toString() + RED + "Hello " + BLUE + BOLD + "World" + YELLOW + BOLD + "!", ChatColor.WHITE.toString() + ChatColor.RED + "Hello " + ChatColor.BLUE + ChatColor.BOLD + "World" + ChatColor.YELLOW + ChatColor.BOLD + "!",
(component) -> BaseComponent.toLegacyText( component ) (component) -> BaseComponent.toLegacyText( component )
); );
} }
private static <T> void testBuilder(Function<ComponentBuilder, T> componentBuilder, Function<T, String> toPlainTextFunction, String expectedLegacyString, Function<T, String> toLegacyTextFunction) private <T> void testBuilder(Function<ComponentBuilder, T> componentBuilder, Function<T, String> toPlainTextFunction, String expectedLegacyString, Function<T, String> toLegacyTextFunction)
{ {
T component = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( RED ). T component = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( ChatColor.RED ).
append( "World" ).bold( true ).color( BLUE ). append( "World" ).bold( true ).color( ChatColor.BLUE ).
append( "!" ).color( YELLOW ) ); append( "!" ).color( ChatColor.YELLOW ) );
assertEquals( "Hello World!", toPlainTextFunction.apply( component ) ); assertEquals( "Hello World!", toPlainTextFunction.apply( component ) );
assertEquals( expectedLegacyString, toLegacyTextFunction.apply( component ) ); assertEquals( expectedLegacyString, toLegacyTextFunction.apply( component ) );
@@ -609,7 +612,7 @@ public class ComponentsTest
@Test @Test
public void testBuilderCreateReset() public void testBuilderCreateReset()
{ {
testBuilderReset( this.testBuilderReset(
ComponentBuilder::create, ComponentBuilder::create,
(components, index) -> components[index] (components, index) -> components[index]
); );
@@ -618,25 +621,25 @@ public class ComponentsTest
@Test @Test
public void testBuilderBuildReset() public void testBuilderBuildReset()
{ {
testBuilderReset( this.testBuilderReset(
ComponentBuilder::build, ComponentBuilder::build,
(component, index) -> component.getExtra().get( index ) (component, index) -> component.getExtra().get( index )
); );
} }
private static <T> void testBuilderReset(Function<ComponentBuilder, T> componentBuilder, BiFunction<T, Integer, BaseComponent> extraGetter) private <T> void testBuilderReset(Function<ComponentBuilder, T> componentBuilder, BiFunction<T, Integer, BaseComponent> extraGetter)
{ {
T component = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( RED ) T component = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( ChatColor.RED )
.append( "World" ).reset() ); .append( "World" ).reset() );
assertEquals( RED, extraGetter.apply( component, 0 ).getColor() ); assertEquals( ChatColor.RED, extraGetter.apply( component, 0 ).getColor() );
assertEquals( WHITE, extraGetter.apply( component, 1 ).getColor() ); assertEquals( ChatColor.WHITE, extraGetter.apply( component, 1 ).getColor() );
} }
@Test @Test
public void testBuilderCreateFormatRetention() public void testBuilderCreateFormatRetention()
{ {
testBuilderFormatRetention( this.testBuilderFormatRetention(
ComponentBuilder::create, ComponentBuilder::create,
(components, index) -> components[index] (components, index) -> components[index]
); );
@@ -645,39 +648,39 @@ public class ComponentsTest
@Test @Test
public void testBuilderBuildFormatRetention() public void testBuilderBuildFormatRetention()
{ {
testBuilderFormatRetention( this.testBuilderFormatRetention(
ComponentBuilder::build, ComponentBuilder::build,
(component, index) -> component.getExtra().get( index ) (component, index) -> component.getExtra().get( index )
); );
} }
private static <T> void testBuilderFormatRetention(Function<ComponentBuilder, T> componentBuilder, BiFunction<T, Integer, BaseComponent> extraGetter) private <T> void testBuilderFormatRetention(Function<ComponentBuilder, T> componentBuilder, BiFunction<T, Integer, BaseComponent> extraGetter)
{ {
T noneRetention = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( RED ) T noneRetention = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( ChatColor.RED )
.append( "World", ComponentBuilder.FormatRetention.NONE ) ); .append( "World", ComponentBuilder.FormatRetention.NONE ) );
assertEquals( RED, extraGetter.apply( noneRetention, 0 ).getColor() ); assertEquals( ChatColor.RED, extraGetter.apply( noneRetention, 0 ).getColor() );
assertEquals( WHITE, extraGetter.apply( noneRetention, 1 ).getColor() ); assertEquals( ChatColor.WHITE, extraGetter.apply( noneRetention, 1 ).getColor() );
HoverEvent testEvent = new HoverEvent( HoverEvent.Action.SHOW_TEXT, new Text( new ComponentBuilder( "test" ).build() ) ); HoverEvent testEvent = new HoverEvent( HoverEvent.Action.SHOW_TEXT, new Text( new ComponentBuilder( "test" ).build() ) );
T formattingRetention = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( RED ) T formattingRetention = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( ChatColor.RED )
.event( testEvent ).append( "World", ComponentBuilder.FormatRetention.FORMATTING ) ); .event( testEvent ).append( "World", ComponentBuilder.FormatRetention.FORMATTING ) );
assertEquals( RED, extraGetter.apply( formattingRetention, 0 ).getColor() ); assertEquals( ChatColor.RED, extraGetter.apply( formattingRetention, 0 ).getColor() );
assertEquals( testEvent, extraGetter.apply( formattingRetention, 0 ).getHoverEvent() ); assertEquals( testEvent, extraGetter.apply( formattingRetention, 0 ).getHoverEvent() );
assertEquals( RED, extraGetter.apply( formattingRetention, 1 ).getColor() ); assertEquals( ChatColor.RED, extraGetter.apply( formattingRetention, 1 ).getColor() );
assertNull( extraGetter.apply( formattingRetention, 1 ).getHoverEvent() ); assertNull( extraGetter.apply( formattingRetention, 1 ).getHoverEvent() );
ClickEvent testClickEvent = new ClickEvent( ClickEvent.Action.OPEN_URL, "http://www.example.com" ); ClickEvent testClickEvent = new ClickEvent( ClickEvent.Action.OPEN_URL, "http://www.example.com" );
T eventRetention = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( RED ) T eventRetention = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( ChatColor.RED )
.event( testEvent ).event( testClickEvent ).append( "World", ComponentBuilder.FormatRetention.EVENTS ) ); .event( testEvent ).event( testClickEvent ).append( "World", ComponentBuilder.FormatRetention.EVENTS ) );
assertEquals( RED, extraGetter.apply( eventRetention, 0 ).getColor() ); assertEquals( ChatColor.RED, extraGetter.apply( eventRetention, 0 ).getColor() );
assertEquals( testEvent, extraGetter.apply( eventRetention, 0 ).getHoverEvent() ); assertEquals( testEvent, extraGetter.apply( eventRetention, 0 ).getHoverEvent() );
assertEquals( testClickEvent, extraGetter.apply( eventRetention, 0 ).getClickEvent() ); assertEquals( testClickEvent, extraGetter.apply( eventRetention, 0 ).getClickEvent() );
assertEquals( WHITE, extraGetter.apply( eventRetention, 1 ).getColor() ); assertEquals( ChatColor.WHITE, extraGetter.apply( eventRetention, 1 ).getColor() );
assertEquals( testEvent, extraGetter.apply( eventRetention, 1 ).getHoverEvent() ); assertEquals( testEvent, extraGetter.apply( eventRetention, 1 ).getHoverEvent() );
assertEquals( testClickEvent, extraGetter.apply( eventRetention, 1 ).getClickEvent() ); assertEquals( testClickEvent, extraGetter.apply( eventRetention, 1 ).getClickEvent() );
} }
@@ -695,9 +698,9 @@ public class ComponentsTest
{ {
TextComponent a = new TextComponent( "A" ); TextComponent a = new TextComponent( "A" );
TextComponent b = new TextComponent( "B" ); TextComponent b = new TextComponent( "B" );
b.setColor( AQUA ); b.setColor( ChatColor.AQUA );
TextComponent c = new TextComponent( "C" ); TextComponent c = new TextComponent( "C" );
c.setColor( RED ); c.setColor( ChatColor.RED );
a.addExtra( b ); a.addExtra( b );
b.addExtra( c ); b.addExtra( c );
c.addExtra( a ); c.addExtra( a );
@@ -709,7 +712,7 @@ public class ComponentsTest
{ {
TextComponent a = new TextComponent( "A" ); TextComponent a = new TextComponent( "A" );
TextComponent b = new TextComponent( "B" ); TextComponent b = new TextComponent( "B" );
b.setColor( AQUA ); b.setColor( ChatColor.AQUA );
a.addExtra( b ); a.addExtra( b );
a.addExtra( b ); a.addExtra( b );
ComponentSerializer.toString( a ); ComponentSerializer.toString( a );
@@ -720,9 +723,9 @@ public class ComponentsTest
{ {
TextComponent a = new TextComponent( "A" ); TextComponent a = new TextComponent( "A" );
TextComponent b = new TextComponent( "B" ); TextComponent b = new TextComponent( "B" );
b.setColor( AQUA ); b.setColor( ChatColor.AQUA );
TextComponent c = new TextComponent( "C" ); TextComponent c = new TextComponent( "C" );
c.setColor( RED ); c.setColor( ChatColor.RED );
a.addExtra( b ); a.addExtra( b );
a.addExtra( c ); a.addExtra( c );
c.addExtra( a ); c.addExtra( a );
@@ -738,15 +741,15 @@ public class ComponentsTest
// collect all invalid color codes (e.g. §z, §g, ...) // collect all invalid color codes (e.g. §z, §g, ...)
for ( char alphChar : "0123456789abcdefghijklmnopqrstuvwxyz".toCharArray() ) for ( char alphChar : "0123456789abcdefghijklmnopqrstuvwxyz".toCharArray() )
{ {
if ( ALL_CODES.indexOf( alphChar ) == -1 ) if ( ChatColor.ALL_CODES.indexOf( alphChar ) == -1 )
{ {
allInvalidColorCodes.append( COLOR_CHAR ); allInvalidColorCodes.append( ChatColor.COLOR_CHAR );
allInvalidColorCodes.append( alphChar ); allInvalidColorCodes.append( alphChar );
} }
} }
// last char is a single '§' // last char is a single '§'
allInvalidColorCodes.append( COLOR_CHAR ); allInvalidColorCodes.append( ChatColor.COLOR_CHAR );
String invalidColorCodesLegacyText = fromAndToLegacyText( allInvalidColorCodes.toString() ); String invalidColorCodesLegacyText = fromAndToLegacyText( allInvalidColorCodes.toString() );
String emptyLegacyText = fromAndToLegacyText( "" ); String emptyLegacyText = fromAndToLegacyText( "" );
@@ -758,10 +761,10 @@ public class ComponentsTest
@Test @Test
public void testFormattingOnlyTextConversion() public void testFormattingOnlyTextConversion()
{ {
String text = "" + GREEN; String text = "§a";
BaseComponent[] converted = TextComponent.fromLegacyText( text ); BaseComponent[] converted = TextComponent.fromLegacyText( text );
assertEquals( GREEN, converted[0].getColor() ); assertEquals( ChatColor.GREEN, converted[0].getColor() );
String roundtripLegacyText = BaseComponent.toLegacyText( converted ); String roundtripLegacyText = BaseComponent.toLegacyText( converted );
@@ -796,8 +799,8 @@ public class ComponentsTest
@Test @Test
public void testLegacyHack() public void testLegacyHack()
{ {
BaseComponent[] hexColored = new ComponentBuilder().color( of( Color.GRAY ) ).append( "Test" ).create(); BaseComponent[] hexColored = new ComponentBuilder().color( ChatColor.of( Color.GRAY ) ).append( "Test" ).create();
String legacy = BaseComponent.toLegacyText( hexColored ); String legacy = TextComponent.toLegacyText( hexColored );
BaseComponent[] reColored = TextComponent.fromLegacyText( legacy ); BaseComponent[] reColored = TextComponent.fromLegacyText( legacy );
@@ -807,7 +810,7 @@ public class ComponentsTest
@Test @Test
public void testLegacyResetInBuilderCreate() public void testLegacyResetInBuilderCreate()
{ {
testLegacyResetInBuilder( this.testLegacyResetInBuilder(
ComponentBuilder::create, ComponentBuilder::create,
ComponentSerializer::toString ComponentSerializer::toString
); );
@@ -816,7 +819,7 @@ public class ComponentsTest
@Test @Test
public void testLegacyResetInBuilderBuild() public void testLegacyResetInBuilderBuild()
{ {
testLegacyResetInBuilder( this.testLegacyResetInBuilder(
ComponentBuilder::build, ComponentBuilder::build,
ComponentSerializer::toString ComponentSerializer::toString
); );
@@ -848,10 +851,10 @@ public class ComponentsTest
* In legacy chat, colors and reset both reset all formatting. * In legacy chat, colors and reset both reset all formatting.
* Make sure it works in combination with ComponentBuilder. * Make sure it works in combination with ComponentBuilder.
*/ */
private static <T> void testLegacyResetInBuilder(Function<ComponentBuilder, T> componentBuilder, Function<T, String> componentSerializer) private <T> void testLegacyResetInBuilder(Function<ComponentBuilder, T> componentBuilder, Function<T, String> componentSerializer)
{ {
ComponentBuilder builder = new ComponentBuilder(); ComponentBuilder builder = new ComponentBuilder();
BaseComponent[] a = TextComponent.fromLegacyText( "" + DARK_RED + UNDERLINE + "44444" + RESET + "dd" + GOLD + BOLD + "6666" ); BaseComponent[] a = TextComponent.fromLegacyText( "§4§n44444§rdd§6§l6666" );
String expected = "{\"extra\":[{\"underlined\":true,\"color\":\"dark_red\",\"text\":\"44444\"},{\"color\":" String expected = "{\"extra\":[{\"underlined\":true,\"color\":\"dark_red\",\"text\":\"44444\"},{\"color\":"
+ "\"white\",\"text\":\"dd\"},{\"bold\":true,\"color\":\"gold\",\"text\":\"6666\"}],\"text\":\"\"}"; + "\"white\",\"text\":\"dd\"},{\"bold\":true,\"color\":\"gold\",\"text\":\"6666\"}],\"text\":\"\"}";
@@ -862,7 +865,7 @@ public class ComponentsTest
String test1 = componentSerializer.apply( componentBuilder.apply( builder ) ); String test1 = componentSerializer.apply( componentBuilder.apply( builder ) );
assertEquals( expected, test1 ); assertEquals( expected, test1 );
BaseComponent[] b = TextComponent.fromLegacyText( RESET + "rrrr" ); BaseComponent[] b = TextComponent.fromLegacyText( "§rrrrr" );
builder.append( b ); builder.append( b );
String test2 = componentSerializer.apply( componentBuilder.apply( builder ) ); String test2 = componentSerializer.apply( componentBuilder.apply( builder ) );
@@ -877,11 +880,4 @@ public class ComponentsTest
{ {
return BaseComponent.toLegacyText( TextComponent.fromLegacyText( legacyText ) ); return BaseComponent.toLegacyText( TextComponent.fromLegacyText( legacyText ) );
} }
@Test
public void testArrayParsing()
{
assertEquals( "Outfluencer is very cool bdfg28dhzcathisisacoolcomponent",
ComponentSerializer.deserialize( "[Outfluencer,[\" \",is,[\" very\",\" cool \",[b,dfg28dhz,[c,[a,thisisacoolcomponent]]]]]]" ).toPlainText() );
}
} }

View File

@@ -0,0 +1,28 @@
package net.md_5.bungee.api.chat;
import static org.junit.jupiter.api.Assertions.*;
import net.md_5.bungee.chat.ComponentSerializer;
import org.junit.jupiter.api.Test;
public class TranslatableComponentTest
{
@Test
public void testMissingPlaceholdersAdded()
{
TranslatableComponent testComponent = new TranslatableComponent( "Test string with %s placeholders: %s", 2, "aoeu" );
assertEquals( "Test string with 2 placeholders: aoeu", testComponent.toPlainText() );
assertEquals( "§fTest string with §f2§f placeholders: §faoeu", testComponent.toLegacyText() );
}
@Test
public void testJsonSerialisation()
{
TranslatableComponent testComponent = new TranslatableComponent( "Test string with %s placeholder", "a" );
String jsonString = ComponentSerializer.toString( testComponent );
BaseComponent[] baseComponents = ComponentSerializer.parse( jsonString );
assertEquals( "Test string with a placeholder", TextComponent.toPlainText( baseComponents ) );
assertEquals( "§fTest string with §fa§f placeholder", TextComponent.toLegacyText( baseComponents ) );
}
}

View File

@@ -4,15 +4,14 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>net.md-5</groupId> <groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-parent</artifactId> <artifactId>bungeecord-parent</artifactId>
<version>1.21-R0.4-SNAPSHOT</version> <version>1.20-R0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-config</artifactId> <artifactId>bungeecord-config</artifactId>
<version>1.21-R0.4-SNAPSHOT</version> <version>1.20-R0.3-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>BungeeCord-Config</name> <name>BungeeCord-Config</name>
@@ -22,7 +21,7 @@
<dependency> <dependency>
<groupId>com.google.code.gson</groupId> <groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId> <artifactId>gson</artifactId>
<version>2.11.0</version> <version>2.10.1</version>
<scope>compile</scope> <scope>compile</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>

View File

@@ -1,28 +0,0 @@
BSD 3-Clause License
Copyright (c) 2025, SpigotMC Pty. Ltd.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,38 +0,0 @@
BungeeCord-Dialog
=================
Highly experimental API, subject to breakage. All contributions welcome, including major refactors/design changes.
Sample Plugin
-------------
```java
private class TestCommand extends Command
{
public TestCommand()
{
super( "btest" );
}
@Override
public void execute(CommandSender sender, String[] args)
{
ProxiedPlayer player = (ProxiedPlayer) sender;
Dialog notice = new NoticeDialog( new DialogBase( new ComponentBuilder( "Hello" ).color( ChatColor.RED ).build() ) );
player.showDialog( notice );
notice = new NoticeDialog(
new DialogBase( new ComponentBuilder( "Hello" ).color( ChatColor.RED ).build() )
.inputs(
Arrays.asList( new TextInput( "first", new ComponentBuilder( "First" ).build() ),
new TextInput( "second", new ComponentBuilder( "Second" ).build() )
)
) )
.action( new ActionButton( new ComponentBuilder( "Submit Button" ).build(), new CustomClickAction( "customform" ) ) );
player.sendMessage( new ComponentBuilder( "click me" ).event( new ShowDialogClickEvent( notice ) ).build() );
}
}
```

View File

@@ -1,31 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-shared-configuration>
<!--
This file contains additional configuration written by modules in the NetBeans IDE.
The configuration is intended to be shared among all the users of project and
therefore it is assumed to be part of version control checkout.
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
-->
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
<!--
Properties that influence various parts of the IDE, especially code formatting and the like.
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
That way multiple projects can share the same settings (useful for formatting rules for example).
Any value defined here will override the pom.xml file value but is only applicable to the current project.
-->
<org-netbeans-modules-editor-indent.CodeStyle.usedProfile>project</org-netbeans-modules-editor-indent.CodeStyle.usedProfile>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>
</properties>
</project-shared-configuration>

View File

@@ -1,36 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-parent</artifactId>
<version>1.21-R0.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-dialog</artifactId>
<version>1.21-R0.4-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-Dialog</name>
<description>Minecraft dialog API intended for use with BungeeCord</description>
<licenses>
<license>
<name>BSD-3-Clause</name>
<url>https://github.com/SpigotMC/BungeeCord/blob/master/dialog/LICENSE</url>
<distribution>repo</distribution>
</license>
</licenses>
<dependencies>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-chat</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@@ -1,39 +0,0 @@
package net.md_5.bungee.api.dialog;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.ToString;
import lombok.experimental.Accessors;
import net.md_5.bungee.api.dialog.action.ActionButton;
/**
* Represents a simple dialog with text and two actions at the bottom (default:
* "yes", "no").
*/
@Data
@ToString
@EqualsAndHashCode
@AllArgsConstructor
@Accessors(fluent = true)
public final class ConfirmationDialog implements Dialog
{
@NonNull
@Accessors(fluent = false)
private DialogBase base;
/**
* The "yes" click action / bottom (appears on the left).
*/
private ActionButton yes;
/**
* The "no" click action / bottom (appears on the right).
*/
private ActionButton no;
public ConfirmationDialog(@NonNull DialogBase base)
{
this( base, null, null );
}
}

View File

@@ -1,29 +0,0 @@
package net.md_5.bungee.api.dialog;
import org.jetbrains.annotations.ApiStatus;
/**
* Represents a dialog GUI.
*/
public interface Dialog
{
/**
* Gets the dialog base which contains the dialog title and other options
* common to all types of dialogs.
*
* @return mutable reference to the dialog base
*/
DialogBase getBase();
/**
* Sets the dialog base.
* <br>
* For internal use only as this is mandatory and should be specified in the
* constructor.
*
* @param base the new dialog base
*/
@ApiStatus.Internal
void setBase(DialogBase base);
}

View File

@@ -1,84 +0,0 @@
package net.md_5.bungee.api.dialog;
import com.google.gson.annotations.SerializedName;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NonNull;
import lombok.experimental.Accessors;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.dialog.body.DialogBody;
import net.md_5.bungee.api.dialog.input.DialogInput;
/**
* Represents the title and other options common to all dialogs.
*/
@Data
@AllArgsConstructor
@Accessors(fluent = true)
public final class DialogBase
{
/**
* The mandatory dialog title.
*/
@NonNull
private BaseComponent title;
/**
* The name which is used for any buttons leading to this dialog (eg from a
* {@link DialogListDialog}). Otherwise defaults to {@link #title}.
*/
@SerializedName("external_title")
private BaseComponent externalTitle;
/**
* The inputs to the dialog.
*/
private List<DialogInput> inputs;
/**
* The body elements which make up this dialog.
*/
private List<DialogBody> body;
/**
* Whether this dialog can be closed with the escape key (default: true).
*/
@SerializedName("can_close_with_escape")
private Boolean canCloseWithEscape;
/**
* Whether this dialog should pause the game in single-player mode (default:
* true).
*/
private Boolean pause;
/**
* Action to take after the a click or submit action is performed on the
* dialog (default: close).
*/
@SerializedName("after_action")
private AfterAction afterAction;
public DialogBase(@NonNull BaseComponent title)
{
this( title, null, null, null, null, null, null );
}
/**
* Types of action which may be taken after the dialog.
*/
public enum AfterAction
{
/**
* Close the dialog.
*/
@SerializedName("close")
CLOSE,
/**
* Do nothing.
*/
@SerializedName("none")
NONE,
/**
* Show a waiting for response screen.
*/
@SerializedName("wait_for_response")
WAIT_FOR_RESPONSE;
}
}

View File

@@ -1,73 +0,0 @@
package net.md_5.bungee.api.dialog;
import com.google.common.base.Preconditions;
import com.google.gson.annotations.SerializedName;
import java.util.Arrays;
import java.util.List;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.ToString;
import lombok.experimental.Accessors;
import net.md_5.bungee.api.dialog.action.ActionButton;
/**
* Represents a dialog which contains buttons that link to other dialogs.
*/
@Data
@ToString
@EqualsAndHashCode
@Accessors(fluent = true)
public final class DialogListDialog implements Dialog
{
@NonNull
@Accessors(fluent = false)
private DialogBase base;
/**
* The child dialogs behind each button.
*/
private List<Dialog> dialogs;
/**
* The {@link ActionButton} activated when the dialog is exited.
*/
@SerializedName("exit_action")
private ActionButton exitAction;
/**
* The number of columns for the dialog buttons (default: 2).
*/
private Integer columns;
/**
* The width of the dialog buttons (default: 150, minimum: 1, maximum: 1024).
*/
@SerializedName("button_width")
private Integer buttonWidth;
public DialogListDialog(@NonNull DialogBase base, Dialog... dialogs)
{
this( base, Arrays.asList( dialogs ), null, null, null );
}
public DialogListDialog(@NonNull DialogBase base, List<Dialog> dialogs, ActionButton exitAction, Integer columns, Integer buttonWidth)
{
this.base = base;
this.dialogs = dialogs;
this.exitAction = exitAction;
columns( columns );
buttonWidth( buttonWidth );
}
public DialogListDialog columns(Integer columns)
{
Preconditions.checkArgument( columns == null || columns > 0, "At least one column is required" );
this.columns = columns;
return this;
}
public DialogListDialog buttonWidth(Integer buttonWidth)
{
Preconditions.checkArgument( buttonWidth == null || ( buttonWidth >= 1 && buttonWidth <= 1024 ), "buttonWidth must be between 1 and 1024" );
this.buttonWidth = buttonWidth;
return this;
}
}

View File

@@ -1,64 +0,0 @@
package net.md_5.bungee.api.dialog;
import com.google.common.base.Preconditions;
import com.google.gson.annotations.SerializedName;
import java.util.Arrays;
import java.util.List;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.ToString;
import lombok.experimental.Accessors;
import net.md_5.bungee.api.dialog.action.ActionButton;
/**
* Represents a dialog with text a list of action buttons grouped into columns
* and scrollable if necessary.
*/
@Data
@ToString
@EqualsAndHashCode
@Accessors(fluent = true)
public final class MultiActionDialog implements Dialog
{
@NonNull
@Accessors(fluent = false)
private DialogBase base;
/**
* The action buttons in the dialog. At least one must be provided.
*/
@NonNull
private List<ActionButton> actions;
/**
* The number of columns for the dialog buttons (default: 2).
*/
private Integer columns;
/**
* The {@link ActionButton} activated when the dialog is exited.
*/
@SerializedName("exit_action")
private ActionButton exitAction;
public MultiActionDialog(@NonNull DialogBase base, @NonNull ActionButton... actions)
{
this( base, Arrays.asList( actions ), null, null );
}
public MultiActionDialog(@NonNull DialogBase base, @NonNull List<ActionButton> actions, Integer columns, ActionButton exitAction)
{
Preconditions.checkArgument( !actions.isEmpty(), "At least one action must be provided" );
this.base = base;
this.actions = actions;
columns( columns );
this.exitAction = exitAction;
}
public MultiActionDialog columns(Integer columns)
{
Preconditions.checkArgument( columns == null || columns > 0, "At least one column is required" );
this.columns = columns;
return this;
}
}

View File

@@ -1,35 +0,0 @@
package net.md_5.bungee.api.dialog;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.ToString;
import lombok.experimental.Accessors;
import net.md_5.bungee.api.dialog.action.ActionButton;
/**
* Represents a simple dialog with text and one action at the bottom (default:
* "OK").
*/
@Data
@ToString
@EqualsAndHashCode
@AllArgsConstructor
@Accessors(fluent = true)
public final class NoticeDialog implements Dialog
{
@NonNull
@Accessors(fluent = false)
private DialogBase base;
/**
* The "OK" action button for the dialog.
*/
private ActionButton action;
public NoticeDialog(DialogBase base)
{
this( base, null );
}
}

View File

@@ -1,71 +0,0 @@
package net.md_5.bungee.api.dialog;
import com.google.common.base.Preconditions;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.ToString;
import lombok.experimental.Accessors;
import net.md_5.bungee.api.dialog.action.ActionButton;
/**
* Represents a dialog which shows the links configured/sent from the server.
*/
@Data
@ToString
@EqualsAndHashCode
@Accessors(fluent = true)
public final class ServerLinksDialog implements Dialog
{
@NonNull
@Accessors(fluent = false)
private DialogBase base;
/**
* The optional {@link ActionButton} for this dialog.
*/
@SerializedName("action")
private ActionButton action;
/**
* The {@link ActionButton} activated when the dialog is exited.
*/
@SerializedName("exit_action")
private ActionButton exitAction;
/**
* The number of columns for the dialog buttons (default: 2).
*/
private Integer columns;
/**
* The width of the dialog buttons (default: 150, minimum: 1, maximum: 1024).
*/
@SerializedName("button_width")
private Integer buttonWidth;
public ServerLinksDialog(@NonNull DialogBase base)
{
this( base, null, null, null );
}
public ServerLinksDialog(@NonNull DialogBase base, ActionButton action, Integer columns, Integer buttonWidth)
{
this.base = base;
this.action = action;
columns( columns );
buttonWidth( buttonWidth );
}
public ServerLinksDialog columns(Integer columns)
{
Preconditions.checkArgument( columns == null || columns > 0, "At least one column is required" );
this.columns = columns;
return this;
}
public ServerLinksDialog buttonWidth(Integer buttonWidth)
{
Preconditions.checkArgument( buttonWidth == null || ( buttonWidth >= 1 && buttonWidth <= 1024 ), "buttonWidth must be between 1 and 1024" );
this.buttonWidth = buttonWidth;
return this;
}
}

View File

@@ -1,6 +0,0 @@
package net.md_5.bungee.api.dialog.action;
public interface Action
{
}

View File

@@ -1,54 +0,0 @@
package net.md_5.bungee.api.dialog.action;
import com.google.common.base.Preconditions;
import lombok.Data;
import lombok.NonNull;
import lombok.experimental.Accessors;
import net.md_5.bungee.api.chat.BaseComponent;
/**
* Represents a dialog action which will usually appear as a button.
*/
@Data
@Accessors(fluent = true)
public class ActionButton
{
/**
* The text label of the button, mandatory.
*/
@NonNull
private BaseComponent label;
/**
* The hover tooltip of the button.
*/
private BaseComponent tooltip;
/**
* The width of the button (default: 150, minimum: 1, maximum: 1024).
*/
private Integer width;
/**
* The action to take.
*/
@NonNull
private Action action;
public ActionButton(@NonNull BaseComponent label, BaseComponent tooltip, Integer width, @NonNull Action action)
{
this.label = label;
this.tooltip = tooltip;
setWidth( width );
this.action = action;
}
public ActionButton(@NonNull BaseComponent label, @NonNull Action action)
{
this( label, null, null, action );
}
public void setWidth(Integer width)
{
Preconditions.checkArgument( width == null || ( width >= 1 && width <= 1024 ), "width must be between 1 and 1024" );
this.width = width;
}
}

View File

@@ -1,27 +0,0 @@
package net.md_5.bungee.api.dialog.action;
import com.google.gson.JsonElement;
import lombok.Data;
import lombok.NonNull;
import lombok.ToString;
import lombok.experimental.Accessors;
/**
* Submits the dialog with the given ID and values as a payload.
*/
@Data
@Accessors(fluent = true)
@ToString(callSuper = true)
public class CustomClickAction implements Action
{
/**
* The namespaced key of the submission.
*/
@NonNull
private String id;
/**
* Fields to be added to the submission payload.
*/
private JsonElement additions;
}

View File

@@ -1,25 +0,0 @@
package net.md_5.bungee.api.dialog.action;
import lombok.Data;
import lombok.NonNull;
import lombok.ToString;
import lombok.experimental.Accessors;
/**
* Executes a command. If the command requires a permission
* higher than 0, a confirmation dialog will be shown by the client.
*/
@Data
@Accessors(fluent = true)
@ToString(callSuper = true)
public class RunCommandAction implements Action
{
/**
* The template to be applied, where variables of the form
* <code>$(key)</code> will be replaced by their
* {@link net.md_5.bungee.api.dialog.input.DialogInput#key} value.
*/
@NonNull
private String template;
}

View File

@@ -1,20 +0,0 @@
package net.md_5.bungee.api.dialog.action;
import lombok.Data;
import lombok.NonNull;
import lombok.ToString;
import lombok.experimental.Accessors;
import net.md_5.bungee.api.chat.ClickEvent;
/**
* Represents a static dialog action.
*/
@Data
@Accessors(fluent = true)
@ToString(callSuper = true)
public class StaticAction implements Action
{
@NonNull
private ClickEvent clickEvent;
}

View File

@@ -1,4 +0,0 @@
/**
* Contains the different actions/buttons for a {@link net.md_5.bungee.api.dialog.Dialog}.
*/
package net.md_5.bungee.api.dialog.action;

View File

@@ -1,20 +0,0 @@
package net.md_5.bungee.api.dialog.body;
import lombok.Data;
import lombok.NonNull;
import org.jetbrains.annotations.ApiStatus;
/**
* Represents the body content of a {@link net.md_5.bungee.api.dialog.Dialog}.
*/
@Data
public abstract class DialogBody
{
/**
* The internal body type.
*/
@NonNull
@ApiStatus.Internal
private final String type;
}

View File

@@ -1,49 +0,0 @@
package net.md_5.bungee.api.dialog.body;
import com.google.common.base.Preconditions;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.ToString;
import lombok.experimental.Accessors;
import net.md_5.bungee.api.chat.BaseComponent;
/**
* Represents a dialog body which consists of text constrained to a certain
* width.
*/
@Data
@Accessors(fluent = true)
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class PlainMessageBody extends DialogBody
{
/**
* The text body.
*/
@NonNull
private BaseComponent contents;
/**
* The maximum width (default: 200, minimum: 1, maximum: 1024).
*/
private Integer width;
public PlainMessageBody(@NonNull BaseComponent contents)
{
this( contents, null );
}
public PlainMessageBody(@NonNull BaseComponent contents, Integer width)
{
super( "minecraft:plain_message" );
this.contents = contents;
width( width );
}
public void width(Integer width)
{
Preconditions.checkArgument( width == null || ( width >= 1 && width <= 1024 ), "width must be between 1 and 1024" );
this.width = width;
}
}

View File

@@ -1,5 +0,0 @@
/**
* Contains the different {@link net.md_5.bungee.api.dialog.Dialog} body content
* types.
*/
package net.md_5.bungee.api.dialog.body;

View File

@@ -1,42 +0,0 @@
package net.md_5.bungee.api.dialog.chat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.dialog.Dialog;
/**
* Click event which displays either a pre-existing dialog by key or a custom
* dialog.
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class ShowDialogClickEvent extends ClickEvent
{
/**
* Key for a pre-existing dialog to show.
*/
private String reference;
/**
* Dialog to show.
*/
private Dialog dialog;
public ShowDialogClickEvent(String reference)
{
this( reference, null );
}
public ShowDialogClickEvent(Dialog dialog)
{
this( null, dialog );
}
private ShowDialogClickEvent(String reference, Dialog dialog)
{
super( Action.SHOW_DIALOG, null );
this.reference = reference;
this.dialog = dialog;
}
}

View File

@@ -1,4 +0,0 @@
/**
* Contains dialog extensions to the chat API.
*/
package net.md_5.bungee.api.dialog.chat;

View File

@@ -1,54 +0,0 @@
package net.md_5.bungee.api.dialog.input;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.ToString;
import lombok.experimental.Accessors;
import net.md_5.bungee.api.chat.BaseComponent;
/**
* Represents a checkbox input control.
*/
@Data
@Accessors(fluent = true)
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class BooleanInput extends DialogInput
{
/**
* The input label.
*/
@NonNull
private BaseComponent label;
/**
* The initial value (default: false/unchecked).
*/
private Boolean initial;
/**
* The string value to be submitted when true/checked (default: "true").
*/
@SerializedName("on_true")
private String onTrue;
/**
* The string value to be submitted when false/unchecked (default: "false").
*/
@SerializedName("on_false")
private String onFalse;
public BooleanInput(@NonNull String key, @NonNull BaseComponent label)
{
this( key, label, null, "true", "false" );
}
public BooleanInput(@NonNull String key, @NonNull BaseComponent label, Boolean initial, String onTrue, String onFalse)
{
super( "minecraft:boolean", key );
this.label = label;
this.initial = initial;
this.onTrue = onTrue;
this.onFalse = onFalse;
}
}

View File

@@ -1,29 +0,0 @@
package net.md_5.bungee.api.dialog.input;
import lombok.Data;
import lombok.NonNull;
import lombok.experimental.Accessors;
import org.jetbrains.annotations.ApiStatus;
/**
* Represents a type of input which may be displayed/submitted with a form
* dialog.
*/
@Data
@Accessors(fluent = true)
public class DialogInput
{
/**
* The internal input type.
*/
@NonNull
@ApiStatus.Internal
private final String type;
/**
* The key corresponding to this input and associated with the value
* submitted.
*/
@NonNull
private final String key;
}

View File

@@ -1,39 +0,0 @@
package net.md_5.bungee.api.dialog.input;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NonNull;
import lombok.experimental.Accessors;
import net.md_5.bungee.api.chat.BaseComponent;
/**
* Represents an option choice which may form part of a
* {@link SingleOptionInput}.
*/
@Data
@AllArgsConstructor
@Accessors(fluent = true)
public class InputOption
{
/**
* The string value associated with this option, to be submitted when
* selected.
*/
@NonNull
private String id;
/**
* The text to display for this option.
*/
private BaseComponent display;
/**
* Whether this option is the one initially selected. Only one option may
* have this value as true (default: first option).
*/
private Boolean initial;
public InputOption(@NonNull String id)
{
this( id, null, null );
}
}

View File

@@ -1,103 +0,0 @@
package net.md_5.bungee.api.dialog.input;
import com.google.common.base.Preconditions;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.ToString;
import lombok.experimental.Accessors;
import net.md_5.bungee.api.chat.BaseComponent;
/**
* Represents a number slider input.
*/
@Data
@Accessors(fluent = true)
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class NumberRangeInput extends DialogInput
{
/**
* The width of the input (default: 200, minimum: 1, maximum: 1024).
*/
private Integer width;
/**
* The label of the slider.
*/
@NonNull
private BaseComponent label;
/**
* A translate key used to display the label value (default:
* options.generic_value).
*/
private String labelFormat;
/**
* The start position of the slider (leftmost position).
*/
private float start;
/**
* The end position of the slider (rightmost position).
*/
private float end;
/**
* The steps in which the input will be increased or decreased, or null if
* no specific steps.
*/
private Float step;
/**
* The initial value of number input, or null to fall back to the middle.
*/
private Float initial;
public NumberRangeInput(@NonNull String key, @NonNull BaseComponent label, float start, float end)
{
this( key, null, label, "options.generic_value", start, end, null, null );
}
public NumberRangeInput(@NonNull String key, @NonNull BaseComponent label, float start, float end, Float step)
{
this( key, null, label, "options.generic_value", start, end, step, null );
}
public NumberRangeInput(@NonNull String key, @NonNull BaseComponent label, float start, float end, Float step, Float initial)
{
this( key, null, label, "options.generic_value", start, end, step, initial );
}
public NumberRangeInput(@NonNull String key, Integer width, @NonNull BaseComponent label, String labelFormat, float start, float end, Float step, Float initial)
{
super( "minecraft:number_range", key );
width( width );
this.label = label;
this.labelFormat = labelFormat;
this.start = start;
this.end = end;
step( step );
initial( initial );
}
public NumberRangeInput width(Integer width)
{
Preconditions.checkArgument( width == null || ( width >= 1 && width <= 1024 ), "with must be between 1 and 1024" );
this.width = width;
return this;
}
public NumberRangeInput step(Float step)
{
Preconditions.checkArgument( step == null || step > 0, "step must be null or greater than zero" );
this.step = step;
return this;
}
public NumberRangeInput initial(Float initial)
{
// we need to calculate if the initial value is between start and end, regardless of the order
float min = Math.min( start, end );
float max = Math.max( start, end );
Preconditions.checkArgument( initial == null || ( initial >= min && initial <= max ), "initial must be null or between start and end" );
this.initial = initial;
return this;
}
}

View File

@@ -1,66 +0,0 @@
package net.md_5.bungee.api.dialog.input;
import com.google.common.base.Preconditions;
import com.google.gson.annotations.SerializedName;
import java.util.Arrays;
import java.util.List;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.ToString;
import lombok.experimental.Accessors;
import net.md_5.bungee.api.chat.BaseComponent;
/**
* Represents a single option (dropdown) input.
*/
@Data
@Accessors(fluent = true)
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class SingleOptionInput extends DialogInput
{
/**
* The width of the input (default: 200, minimum: 1, maximum: 1024).
*/
private Integer width;
/**
* The input label.
*/
@NonNull
private BaseComponent label;
/**
* Whether the label is visible (default: true).
*/
@SerializedName("label_visible")
private Boolean labelVisible;
/**
* The non-empty list of options to be selected from.
*/
@NonNull
private List<InputOption> options;
public SingleOptionInput(@NonNull String key, @NonNull BaseComponent label, @NonNull InputOption... options)
{
this( key, null, label, null, Arrays.asList( options ) );
}
public SingleOptionInput(@NonNull String key, Integer width, @NonNull BaseComponent label, Boolean labelVisible, @NonNull List<InputOption> options)
{
super( "minecraft:single_option", key );
Preconditions.checkArgument( !options.isEmpty(), "At least one option must be provided" );
width( width );
this.label = label;
this.labelVisible = labelVisible;
this.options = options;
}
public SingleOptionInput width(Integer width)
{
Preconditions.checkArgument( width == null || ( width >= 1 && width <= 1024 ), "width must be between 1 and 1024" );
this.width = width;
return this;
}
}

View File

@@ -1,110 +0,0 @@
package net.md_5.bungee.api.dialog.input;
import com.google.common.base.Preconditions;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.ToString;
import lombok.experimental.Accessors;
import net.md_5.bungee.api.chat.BaseComponent;
/**
* Represents a textbox input.
*/
@Data
@Accessors(fluent = true)
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class TextInput extends DialogInput
{
/**
* The width of this text input (default: 200, minimum: 1, maximum: 1024).
*/
private Integer width;
/**
* The label of this text input.
*/
@NonNull
private BaseComponent label;
/**
* The visibility of this text input's label.
*/
@SerializedName("label_visible")
private Boolean labelVisible;
/**
* The initial value of this text input.
*/
private String initial;
/**
* The maximum length of the input (default: 32).
*/
@SerializedName("max_length")
private Integer maxLength;
/**
* If present, allows users to input multiple lines.
*/
private Multiline multiline;
public TextInput(@NonNull String key, @NonNull BaseComponent label)
{
this( key, null, label, null, null, null, null );
}
public TextInput(@NonNull String key, Integer width, @NonNull BaseComponent label, Boolean labelVisible, String initial, Integer maxLength)
{
this( key, width, label, labelVisible, initial, maxLength, null );
}
public TextInput(@NonNull String key, Integer width, @NonNull BaseComponent label, Boolean labelVisible, String initial, Integer maxLength, Multiline multiline)
{
super( "minecraft:text", key );
width( width );
this.label = label;
this.labelVisible = labelVisible;
this.initial = initial;
this.maxLength = maxLength;
this.multiline = multiline;
}
/**
* Configuration data for a multiline input.
*/
@Data
@NoArgsConstructor
@Accessors(fluent = true)
public static class Multiline
{
/**
* The maximum length of input, or null to disable any limits.
*/
@SerializedName("max_lines")
private Integer maxLines;
/**
* The height of this input (default: 32, minimum: 1, maximum: 512).
*/
private Integer height;
public Multiline(Integer maxLines, Integer height)
{
height( height ).maxLines( maxLines );
}
public Multiline height(Integer height)
{
Preconditions.checkArgument( height == null || height >= 1 && height <= 512, "height must null or be between 1 and 512" );
this.height = height;
return this;
}
}
public TextInput width(Integer width)
{
Preconditions.checkArgument( width == null || ( width >= 1 && width <= 1024 ), "width must be between 1 and 1024" );
this.width = width;
return this;
}
}

View File

@@ -1,4 +0,0 @@
/**
* Represents the various input controls which may be present on form dialogs.
*/
package net.md_5.bungee.api.dialog.input;

View File

@@ -1,4 +0,0 @@
/**
* Contains the core classes for the display of a {@link net.md_5.bungee.api.dialog.Dialog}.
*/
package net.md_5.bungee.api.dialog;

View File

@@ -4,15 +4,14 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>net.md-5</groupId> <groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-parent</artifactId> <artifactId>bungeecord-parent</artifactId>
<version>1.21-R0.4-SNAPSHOT</version> <version>1.20-R0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-event</artifactId> <artifactId>bungeecord-event</artifactId>
<version>1.21-R0.4-SNAPSHOT</version> <version>1.20-R0.3-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>BungeeCord-Event</name> <name>BungeeCord-Event</name>

View File

@@ -4,15 +4,14 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>net.md-5</groupId> <groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-parent</artifactId> <artifactId>bungeecord-parent</artifactId>
<version>1.21-R0.4-SNAPSHOT</version> <version>1.20-R0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-log</artifactId> <artifactId>bungeecord-log</artifactId>
<version>1.21-R0.4-SNAPSHOT</version> <version>1.20-R0.3-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>BungeeCord-Log</name> <name>BungeeCord-Log</name>
@@ -20,13 +19,13 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.jline</groupId> <groupId>jline</groupId>
<artifactId>jline</artifactId> <artifactId>jline</artifactId>
<version>3.30.4</version> <version>2.12.1</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.md-5</groupId> <groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-chat</artifactId> <artifactId>bungeecord-chat</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
<scope>compile</scope> <scope>compile</scope>

View File

@@ -1,18 +1,26 @@
package net.md_5.bungee.log; package net.md_5.bungee.log;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException; import java.io.IOException;
import java.util.logging.FileHandler; import java.util.logging.FileHandler;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.LogRecord; import java.util.logging.LogRecord;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.jline.reader.LineReader; import jline.console.ConsoleReader;
public class BungeeLogger extends Logger public class BungeeLogger extends Logger
{ {
private final LogDispatcher dispatcher = new LogDispatcher( this ); private final LogDispatcher dispatcher = new LogDispatcher( this );
public BungeeLogger(String loggerName, String filePattern, LineReader reader) // CHECKSTYLE:OFF
@SuppressWarnings(
{
"CallToPrintStackTrace", "CallToThreadStartDuringObjectConstruction"
})
// CHECKSTYLE:ON
@SuppressFBWarnings("SC_START_IN_CTOR")
public BungeeLogger(String loggerName, String filePattern, ConsoleReader reader)
{ {
super( loggerName, null ); super( loggerName, null );
setLevel( Level.ALL ); setLevel( Level.ALL );

View File

@@ -1,15 +1,15 @@
package net.md_5.bungee.log; package net.md_5.bungee.log;
import java.io.IOException;
import java.util.logging.Handler; import java.util.logging.Handler;
import java.util.logging.LogRecord; import java.util.logging.LogRecord;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import jline.console.ConsoleReader;
import lombok.Data; import lombok.Data;
import lombok.RequiredArgsConstructor;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import org.jline.jansi.Ansi; import org.fusesource.jansi.Ansi;
import org.jline.reader.LineReader; import org.fusesource.jansi.Ansi.Erase;
@RequiredArgsConstructor
public class ColouredWriter extends Handler public class ColouredWriter extends Handler
{ {
@@ -52,7 +52,12 @@ public class ColouredWriter extends Handler
compile( ChatColor.RESET, Ansi.ansi().a( Ansi.Attribute.RESET ).toString() ), compile( ChatColor.RESET, Ansi.ansi().a( Ansi.Attribute.RESET ).toString() ),
}; };
// //
private final LineReader console; private final ConsoleReader console;
public ColouredWriter(ConsoleReader console)
{
this.console = console;
}
public void print(String s) public void print(String s)
{ {
@@ -60,7 +65,14 @@ public class ColouredWriter extends Handler
{ {
s = replacement.pattern.matcher( s ).replaceAll( replacement.replacement ); s = replacement.pattern.matcher( s ).replaceAll( replacement.replacement );
} }
console.printAbove( s + Ansi.ansi().reset().toString() ); try
{
console.print( Ansi.ansi().eraseLine( Erase.ALL ).toString() + ConsoleReader.RESET_LINE + s + Ansi.ansi().reset().toString() );
console.drawLine();
console.flush();
} catch ( IOException ex )
{
}
} }
@Override @Override

View File

@@ -18,6 +18,7 @@ public class ConciseFormatter extends Formatter
private final boolean coloured; private final boolean coloured;
@Override @Override
@SuppressWarnings("ThrowableResultIgnored")
public String format(LogRecord record) public String format(LogRecord record)
{ {
StringBuilder formatted = new StringBuilder(); StringBuilder formatted = new StringBuilder();

View File

@@ -1,31 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-shared-configuration>
<!--
This file contains additional configuration written by modules in the NetBeans IDE.
The configuration is intended to be shared among all the users of project and
therefore it is assumed to be part of version control checkout.
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
-->
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
<!--
Properties that influence various parts of the IDE, especially code formatting and the like.
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
That way multiple projects can share the same settings (useful for formatting rules for example).
Any value defined here will override the pom.xml file value but is only applicable to the current project.
-->
<org-netbeans-modules-editor-indent.CodeStyle.usedProfile>project</org-netbeans-modules-editor-indent.CodeStyle.usedProfile>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>
</properties>
</project-shared-configuration>

View File

@@ -1,20 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module</artifactId>
<version>1.21-R0.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module-cmd-alert</artifactId>
<version>1.21-R0.4-SNAPSHOT</version>
<packaging>jar</packaging>
<name>cmd_alert</name>
<description>Provides the alert and alertraw commands</description>
</project>

View File

@@ -1,47 +0,0 @@
package net.md_5.bungee.module.cmd.alert;
import java.util.Locale;
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.TextComponent;
import net.md_5.bungee.api.plugin.Command;
public class CommandAlert extends Command
{
public CommandAlert()
{
super( "alert", "bungeecord.command.alert" );
}
@Override
public void execute(CommandSender sender, String[] args)
{
if ( args.length == 0 )
{
sender.sendMessage( ProxyServer.getInstance().getTranslation( "message_needed" ) );
} else
{
StringBuilder builder = new StringBuilder();
if ( args[0].toLowerCase( Locale.ROOT ).startsWith( "&h" ) )
{
// Remove &h
args[0] = args[0].substring( 2 );
} else
{
builder.append( ProxyServer.getInstance().getTranslation( "alert" ) );
}
for ( String s : args )
{
builder.append( ChatColor.translateAlternateColorCodes( '&', s ) );
builder.append( " " );
}
String message = builder.substring( 0, builder.length() - 1 );
ProxyServer.getInstance().broadcast( TextComponent.fromLegacy( message ) );
}
}
}

View File

@@ -1,56 +0,0 @@
package net.md_5.bungee.module.cmd.alert;
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.connection.ProxiedPlayer;
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.alertraw" );
}
@Override
public void execute(CommandSender sender, String[] args)
{
if ( args.length == 0 )
{
sender.sendMessage( ProxyServer.getInstance().getTranslation( "message_needed" ) );
} else
{
String message = Joiner.on( ' ' ).join( args );
try
{
ProxyServer.getInstance().broadcast( ComponentSerializer.parse( message ) );
} catch ( Exception e )
{
Throwable error = e;
while ( error.getCause() != null )
{
error = error.getCause();
}
if ( sender instanceof ProxiedPlayer )
{
sender.sendMessage( new ComponentBuilder( ProxyServer.getInstance().getTranslation( "error_occurred_player" ) )
.event( new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( error.getMessage() )
.color( ChatColor.RED )
.create() ) )
.create()
);
} else
{
sender.sendMessage( ProxyServer.getInstance().getTranslation( "error_occurred_console", error.getMessage() ) );
}
}
}
}
}

View File

@@ -1,14 +0,0 @@
package net.md_5.bungee.module.cmd.alert;
import net.md_5.bungee.api.plugin.Plugin;
public class PluginAlert extends Plugin
{
@Override
public void onEnable()
{
getProxy().getPluginManager().registerCommand( this, new CommandAlert() );
getProxy().getPluginManager().registerCommand( this, new CommandAlertRaw() );
}
}

View File

@@ -1,5 +0,0 @@
name: ${project.name}
main: net.md_5.bungee.module.cmd.alert.PluginAlert
version: ${describe}
description: ${project.description}
author: ${module.author}

View File

@@ -1,31 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-shared-configuration>
<!--
This file contains additional configuration written by modules in the NetBeans IDE.
The configuration is intended to be shared among all the users of project and
therefore it is assumed to be part of version control checkout.
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
-->
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
<!--
Properties that influence various parts of the IDE, especially code formatting and the like.
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
That way multiple projects can share the same settings (useful for formatting rules for example).
Any value defined here will override the pom.xml file value but is only applicable to the current project.
-->
<org-netbeans-modules-editor-indent.CodeStyle.usedProfile>project</org-netbeans-modules-editor-indent.CodeStyle.usedProfile>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>
</properties>
</project-shared-configuration>

View File

@@ -1,20 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module</artifactId>
<version>1.21-R0.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module-cmd-find</artifactId>
<version>1.21-R0.4-SNAPSHOT</version>
<packaging>jar</packaging>
<name>cmd_find</name>
<description>Provides the find command</description>
</project>

View File

@@ -1,58 +0,0 @@
package net.md_5.bungee.module.cmd.find;
import java.util.Collections;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
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.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.command.PlayerCommand;
public class CommandFind extends PlayerCommand
{
public CommandFind()
{
super( "find", "bungeecord.command.find" );
}
@Override
public void execute(CommandSender sender, String[] args)
{
if ( args.length != 1 )
{
sender.sendMessage( ProxyServer.getInstance().getTranslation( "username_needed" ) );
} else
{
ProxiedPlayer player = ProxyServer.getInstance().getPlayer( args[0] );
if ( player == null || player.getServer() == null )
{
sender.sendMessage( ProxyServer.getInstance().getTranslation( "user_not_online" ) );
} else
{
boolean moduleLoaded = ProxyServer.getInstance().getPluginManager().getPlugin( "cmd_server" ) != null;
ServerInfo server = player.getServer().getInfo();
ComponentBuilder componentBuilder = new ComponentBuilder().appendLegacy( ProxyServer.getInstance().getTranslation( "user_online_at", player.getName(), server.getName() ) );
if ( moduleLoaded && server.canAccess( sender ) )
{
componentBuilder.event( new HoverEvent(
HoverEvent.Action.SHOW_TEXT,
new ComponentBuilder().appendLegacy( ProxyServer.getInstance().getTranslation( "click_to_connect" ) ).create() )
);
componentBuilder.event( new ClickEvent( ClickEvent.Action.RUN_COMMAND, "/server " + server.getName() ) );
}
sender.sendMessage( componentBuilder.create() );
}
}
}
@Override
public Iterable<String> onTabComplete(CommandSender sender, String[] args)
{
return args.length == 1 ? super.onTabComplete( sender, args ) : Collections.emptyList();
}
}

View File

@@ -1,13 +0,0 @@
package net.md_5.bungee.module.cmd.find;
import net.md_5.bungee.api.plugin.Plugin;
public class PluginFind extends Plugin
{
@Override
public void onEnable()
{
getProxy().getPluginManager().registerCommand( this, new CommandFind() );
}
}

View File

@@ -1,5 +0,0 @@
name: ${project.name}
main: net.md_5.bungee.module.cmd.find.PluginFind
version: ${describe}
description: ${project.description}
author: ${module.author}

View File

@@ -1,31 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-shared-configuration>
<!--
This file contains additional configuration written by modules in the NetBeans IDE.
The configuration is intended to be shared among all the users of project and
therefore it is assumed to be part of version control checkout.
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
-->
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
<!--
Properties that influence various parts of the IDE, especially code formatting and the like.
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
That way multiple projects can share the same settings (useful for formatting rules for example).
Any value defined here will override the pom.xml file value but is only applicable to the current project.
-->
<org-netbeans-modules-editor-indent.CodeStyle.usedProfile>project</org-netbeans-modules-editor-indent.CodeStyle.usedProfile>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>
</properties>
</project-shared-configuration>

View File

@@ -1,20 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module</artifactId>
<version>1.21-R0.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module-cmd-kick</artifactId>
<version>1.21-R0.4-SNAPSHOT</version>
<packaging>jar</packaging>
<name>cmd_kick</name>
<description>Provides the gkick command</description>
</project>

View File

@@ -1,72 +0,0 @@
package net.md_5.bungee.module.cmd.kick;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSet;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
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.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.TabExecutor;
public class CommandKick extends Command implements TabExecutor
{
public CommandKick()
{
super( "gkick", "bungeecord.command.kick" );
}
@Override
public void execute(CommandSender sender, String[] args)
{
if ( args.length == 0 )
{
sender.sendMessage( ProxyServer.getInstance().getTranslation( "username_needed" ) );
} else
{
ProxiedPlayer player = ProxyServer.getInstance().getPlayer( args[0] );
if ( player == null )
{
sender.sendMessage( TextComponent.fromLegacy( ProxyServer.getInstance().getTranslation( "user_not_online" ) ) );
return;
}
if ( args.length == 1 )
{
player.disconnect( TextComponent.fromLegacy( ProxyServer.getInstance().getTranslation( "kick_message" ) ) );
} else
{
String[] reason = new String[ args.length - 1 ];
System.arraycopy( args, 1, reason, 0, reason.length );
player.disconnect( TextComponent.fromLegacy( ChatColor.translateAlternateColorCodes( '&', Joiner.on( ' ' ).join( reason ) ) ) );
}
}
}
@Override
public Iterable<String> onTabComplete(CommandSender sender, String[] args)
{
if ( args.length == 1 )
{
Set<String> matches = new HashSet<>();
String search = args[0].toLowerCase( Locale.ROOT );
for ( ProxiedPlayer player : ProxyServer.getInstance().getPlayers() )
{
if ( player.getName().toLowerCase( Locale.ROOT ).startsWith( search ) )
{
matches.add( player.getName() );
}
}
return matches;
} else
{
return ImmutableSet.of();
}
}
}

View File

@@ -1,13 +0,0 @@
package net.md_5.bungee.module.cmd.kick;
import net.md_5.bungee.api.plugin.Plugin;
public class PluginKick extends Plugin
{
@Override
public void onEnable()
{
getProxy().getPluginManager().registerCommand( this, new CommandKick() );
}
}

View File

@@ -1,5 +0,0 @@
name: ${project.name}
main: net.md_5.bungee.module.cmd.kick.PluginKick
version: ${describe}
description: ${project.description}
author: ${module.author}

View File

@@ -1,31 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-shared-configuration>
<!--
This file contains additional configuration written by modules in the NetBeans IDE.
The configuration is intended to be shared among all the users of project and
therefore it is assumed to be part of version control checkout.
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
-->
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
<!--
Properties that influence various parts of the IDE, especially code formatting and the like.
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
That way multiple projects can share the same settings (useful for formatting rules for example).
Any value defined here will override the pom.xml file value but is only applicable to the current project.
-->
<org-netbeans-modules-editor-indent.CodeStyle.usedProfile>project</org-netbeans-modules-editor-indent.CodeStyle.usedProfile>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>
</properties>
</project-shared-configuration>

View File

@@ -1,20 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module</artifactId>
<version>1.21-R0.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module-cmd-list</artifactId>
<version>1.21-R0.4-SNAPSHOT</version>
<packaging>jar</packaging>
<name>cmd_list</name>
<description>Provides the glist command</description>
</project>

Some files were not shown because too many files have changed in this diff Show More