diff --git a/api/pom.xml b/api/pom.xml
index 43a4dd9432..9ef2b0da29 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -4,13 +4,12 @@
4.0.0
- net.md-5
+ tc.oc
bungeecord-parent
1.11-SNAPSHOT
../pom.xml
- net.md-5
bungeecord-api
1.11-SNAPSHOT
jar
@@ -20,25 +19,30 @@
- net.md-5
+ tc.oc
+ minecraft-api
+ 1.11-SNAPSHOT
+
+
+ tc.oc
bungeecord-chat
${project.version}
compile
- net.md-5
+ tc.oc
bungeecord-config
${project.version}
compile
- net.md-5
+ tc.oc
bungeecord-event
${project.version}
compile
- net.md-5
+ tc.oc
bungeecord-protocol
${project.version}
compile
diff --git a/api/src/main/java/net/md_5/bungee/api/CommandSender.java b/api/src/main/java/net/md_5/bungee/api/CommandSender.java
index a35b3fd0c9..798ca60dda 100644
--- a/api/src/main/java/net/md_5/bungee/api/CommandSender.java
+++ b/api/src/main/java/net/md_5/bungee/api/CommandSender.java
@@ -1,27 +1,10 @@
package net.md_5.bungee.api;
-import net.md_5.bungee.api.chat.BaseComponent;
-
import java.util.Collection;
-public interface CommandSender
+public interface CommandSender extends tc.oc.minecraft.api.command.CommandSender
{
- /**
- * Get the unique name of this command sender.
- *
- * @return the senders username
- */
- public String getName();
-
- /**
- * Send a message to this sender.
- *
- * @param message the message to send
- */
- @Deprecated
- public void sendMessage(String message);
-
/**
* Send several messages to this sender. Each message will be sent
* separately.
@@ -31,20 +14,6 @@ public interface CommandSender
@Deprecated
public void sendMessages(String... messages);
- /**
- * Send a message to this sender.
- *
- * @param message the message to send
- */
- public void sendMessage(BaseComponent... message);
-
- /**
- * Send a message to this sender.
- *
- * @param message the message to send
- */
- public void sendMessage(BaseComponent message);
-
/**
* Get all groups this user is part of. This returns an unmodifiable
* collection.
@@ -67,14 +36,6 @@ public interface CommandSender
*/
public void removeGroups(String... groups);
- /**
- * Checks if this user has the specified permission node.
- *
- * @param permission the node to check
- * @return whether they have this node
- */
- public boolean hasPermission(String permission);
-
/**
* Set a permission node for this user.
*
diff --git a/api/src/main/java/net/md_5/bungee/api/ProxyConfig.java b/api/src/main/java/net/md_5/bungee/api/ProxyConfig.java
index edd82c1e0e..9147e7388d 100644
--- a/api/src/main/java/net/md_5/bungee/api/ProxyConfig.java
+++ b/api/src/main/java/net/md_5/bungee/api/ProxyConfig.java
@@ -32,9 +32,75 @@ public interface ProxyConfig
/**
* Set of all servers.
+ *
+ * @deprecated The returned map may be modified concurrently by the proxy.
+ * The safe alternative is {@link #getServersCopy()}.
*/
+ @Deprecated
Map getServers();
+ /**
+ * Return all servers registered to this proxy, keyed by name. The returned map
+ * is an immutable snapshot of the actual server collection. It cannot be modified,
+ * and it will not change.
+ *
+ * @return all registered remote server destinations
+ */
+ Map getServersCopy();
+
+ /**
+ * Gets the server info of a server.
+ *
+ * @param name the name of the configured server
+ * @return the server info belonging to the specified server
+ */
+ ServerInfo getServerInfo(String name);
+
+ /**
+ * Register the given server to the proxy.
+ * Any currently registered server with the same name will be replaced.
+ *
+ * @return the previously registered server with the same name, or null if there was no such server.
+ */
+ ServerInfo addServer(ServerInfo server);
+
+ /**
+ * Register all of the given servers to the proxy.
+ *
+ * @return true if any servers were added or replaced.
+ */
+ boolean addServers(Collection servers);
+
+ /**
+ * Un-register the server with the given name from the proxy.
+ *
+ * @return the server that was removed, or null if there is no server with the given name.
+ */
+ ServerInfo removeServerNamed(String name);
+
+ /**
+ * Un-register the given server from the proxy.
+ * The server is matched by name only, other fields in the given {@link ServerInfo} are ignored.
+ *
+ * @return the server that was removed, or null if there is no server with a matching name.
+ */
+ ServerInfo removeServer(ServerInfo server);
+
+ /**
+ * Un-register servers with any of the given names from the proxy.
+ *
+ * @return true if any servers were removed.
+ */
+ boolean removeServersNamed(Collection names);
+
+ /**
+ * Un-register all of the given servers from the proxy.
+ * The servers are matched by name only, other fields in the given {@link ServerInfo} are ignored.
+ *
+ * @return true if any servers were removed.
+ */
+ boolean removeServers(Collection servers);
+
/**
* Does the server authenticate with mojang
*/
@@ -79,4 +145,9 @@ public interface ProxyConfig
* The favicon used for the server ping list.
*/
Favicon getFaviconObject();
+
+ /**
+ * Shutdown immediately if any plugins fail to load
+ */
+ boolean isRequireAllPlugins();
}
diff --git a/api/src/main/java/net/md_5/bungee/api/ProxyServer.java b/api/src/main/java/net/md_5/bungee/api/ProxyServer.java
index 11c5b68576..8b02ebab75 100644
--- a/api/src/main/java/net/md_5/bungee/api/ProxyServer.java
+++ b/api/src/main/java/net/md_5/bungee/api/ProxyServer.java
@@ -16,7 +16,7 @@
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.api.scheduler.TaskScheduler;
-public abstract class ProxyServer
+public abstract class ProxyServer implements tc.oc.minecraft.api.server.LocalServer
{
@Getter
@@ -87,15 +87,41 @@ public static void setInstance(ProxyServer instance)
*/
public abstract ProxiedPlayer getPlayer(UUID uuid);
+ @Override
+ public ProxiedPlayer getPlayerExact(String name)
+ {
+ return getPlayer( name );
+ }
+
+ @Override
+ public Collection getOnlinePlayers()
+ {
+ return getPlayers();
+ }
+
/**
* Return all servers registered to this proxy, keyed by name. Unlike the
* methods in {@link ConfigurationAdapter#getServers()}, this will not
* return a fresh map each time.
*
* @return all registered remote server destinations
+ *
+ * @deprecated The returned map is part of the proxy's internal state,
+ * and may be modified concurrently by the proxy.
+ * The safe alternative is {@link #getServersCopy()}.
*/
+ @Deprecated
public abstract Map getServers();
+ /**
+ * Return all servers registered to this proxy, keyed by name. The returned map
+ * is an immutable snapshot of the actual server collection. It cannot be modified,
+ * and it will not change.
+ *
+ * @return all registered remote server destinations
+ */
+ public abstract Map getServersCopy();
+
/**
* Gets the server info of a server.
*
diff --git a/api/src/main/java/net/md_5/bungee/api/ServerPing.java b/api/src/main/java/net/md_5/bungee/api/ServerPing.java
index 27b51849ba..3adfbd1363 100644
--- a/api/src/main/java/net/md_5/bungee/api/ServerPing.java
+++ b/api/src/main/java/net/md_5/bungee/api/ServerPing.java
@@ -140,7 +140,7 @@ public void setFavicon(Favicon favicon)
@Deprecated
public void setDescription(String description)
{
- this.description = new TextComponent( TextComponent.fromLegacyText( description ) );
+ this.description = new TextComponent( TextComponent.fromLegacyText( description, false ) );
}
@Deprecated
diff --git a/api/src/main/java/net/md_5/bungee/api/config/ServerInfo.java b/api/src/main/java/net/md_5/bungee/api/config/ServerInfo.java
index e9f1ff6696..74b2791d78 100644
--- a/api/src/main/java/net/md_5/bungee/api/config/ServerInfo.java
+++ b/api/src/main/java/net/md_5/bungee/api/config/ServerInfo.java
@@ -10,7 +10,7 @@
/**
* Class used to represent a server to connect to.
*/
-public interface ServerInfo
+public interface ServerInfo extends tc.oc.minecraft.api.server.Server
{
/**
diff --git a/api/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java b/api/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java
index 375815c446..891a24b018 100644
--- a/api/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java
+++ b/api/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java
@@ -2,7 +2,7 @@
import java.util.Locale;
import java.util.Map;
-import java.util.UUID;
+
import net.md_5.bungee.api.Callback;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.CommandSender;
@@ -14,24 +14,9 @@
* Represents a player who's connection is being connected to somewhere else,
* whether it be a remote or embedded server.
*/
-public interface ProxiedPlayer extends Connection, CommandSender
+public interface ProxiedPlayer extends Connection, CommandSender, tc.oc.minecraft.api.entity.Player
{
- /**
- * Gets this player's display name.
- *
- * @return the players current display name
- */
- String getDisplayName();
-
- /**
- * Sets this players display name to be used as their nametag and tab list
- * name.
- *
- * @param name the name to set
- */
- void setDisplayName(String name);
-
/**
* Send a message to the specified screen position of this player.
*
@@ -128,13 +113,6 @@ public interface ProxiedPlayer extends Connection, CommandSender
@Deprecated
String getUUID();
- /**
- * Get this connection's UUID, if set.
- *
- * @return the UUID
- */
- UUID getUniqueId();
-
/**
* Gets this player's locale.
*
@@ -211,4 +189,6 @@ public interface ProxiedPlayer extends Connection, CommandSender
* not occurred for this {@link ProxiedPlayer} yet.
*/
Map getModList();
+
+ Throwable getDisconnectException();
}
diff --git a/api/src/main/java/net/md_5/bungee/api/event/ServerConnectEvent.java b/api/src/main/java/net/md_5/bungee/api/event/ServerConnectEvent.java
index 17cfccd0a0..51824022bd 100644
--- a/api/src/main/java/net/md_5/bungee/api/event/ServerConnectEvent.java
+++ b/api/src/main/java/net/md_5/bungee/api/event/ServerConnectEvent.java
@@ -34,6 +34,7 @@ public class ServerConnectEvent extends Event implements Cancellable
* Cancelled state.
*/
private boolean cancelled;
+ private String fakeUsername;
public ServerConnectEvent(ProxiedPlayer player, ServerInfo target)
{
diff --git a/api/src/main/java/net/md_5/bungee/api/plugin/CommandBypassException.java b/api/src/main/java/net/md_5/bungee/api/plugin/CommandBypassException.java
new file mode 100644
index 0000000000..0c3419c825
--- /dev/null
+++ b/api/src/main/java/net/md_5/bungee/api/plugin/CommandBypassException.java
@@ -0,0 +1,8 @@
+package net.md_5.bungee.api.plugin;
+
+/**
+ * Thrown from inside a command to tell the proxy to pass the command upstream
+ */
+public class CommandBypassException extends RuntimeException {
+
+}
diff --git a/api/src/main/java/net/md_5/bungee/api/plugin/Listener.java b/api/src/main/java/net/md_5/bungee/api/plugin/Listener.java
index 31ed4eea7f..a4797ad72f 100644
--- a/api/src/main/java/net/md_5/bungee/api/plugin/Listener.java
+++ b/api/src/main/java/net/md_5/bungee/api/plugin/Listener.java
@@ -3,6 +3,6 @@
/**
* Dummy interface which all event subscribers and listeners must implement.
*/
-public interface Listener
+public interface Listener extends tc.oc.minecraft.api.event.Listener
{
}
diff --git a/api/src/main/java/net/md_5/bungee/api/plugin/Plugin.java b/api/src/main/java/net/md_5/bungee/api/plugin/Plugin.java
index bc91208a7d..5bc19f7294 100644
--- a/api/src/main/java/net/md_5/bungee/api/plugin/Plugin.java
+++ b/api/src/main/java/net/md_5/bungee/api/plugin/Plugin.java
@@ -15,7 +15,7 @@
* Represents any Plugin that may be loaded at runtime to enhance existing
* functionality.
*/
-public class Plugin
+public class Plugin implements tc.oc.minecraft.api.plugin.Plugin
{
@Getter
@@ -50,6 +50,12 @@ public void onDisable()
{
}
+ @Override
+ public ProxyServer getServer()
+ {
+ return getProxy();
+ }
+
/**
* Gets the data folder where this plugin may store arbitrary data. It will
* be a child of {@link ProxyServer#getPluginsFolder()}.
@@ -61,6 +67,12 @@ public final File getDataFolder()
return new File( getProxy().getPluginsFolder(), getDescription().getName() );
}
+ @Override
+ public InputStream getResource(String name)
+ {
+ return getResourceAsStream( name );
+ }
+
/**
* Get a resource from within this plugins jar or container. Care must be
* taken to close the returned stream.
@@ -85,7 +97,7 @@ final void init(ProxyServer proxy, PluginDescription description)
this.proxy = proxy;
this.description = description;
this.file = description.getFile();
- this.logger = new PluginLogger( this );
+ this.logger = PluginLogger.get( this );
}
//
diff --git a/api/src/main/java/net/md_5/bungee/api/plugin/PluginClassloader.java b/api/src/main/java/net/md_5/bungee/api/plugin/PluginClassloader.java
index 85203fb8ad..f21d1ceb58 100644
--- a/api/src/main/java/net/md_5/bungee/api/plugin/PluginClassloader.java
+++ b/api/src/main/java/net/md_5/bungee/api/plugin/PluginClassloader.java
@@ -2,54 +2,54 @@
import java.net.URL;
import java.net.URLClassLoader;
-import java.util.Set;
-import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.List;
public class PluginClassloader extends URLClassLoader
{
-
- private static final Set allLoaders = new CopyOnWriteArraySet<>();
-
static
{
ClassLoader.registerAsParallelCapable();
}
- public PluginClassloader(URL[] urls)
+ private final List dependencies;
+
+ public PluginClassloader(List dependencies, URL[] urls)
{
super( urls );
- allLoaders.add( this );
+ this.dependencies = dependencies;
}
@Override
protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
- return loadClass0( name, resolve, true );
+ synchronized(getClassLoadingLock(name)) {
+ try {
+ return loadLocalClass(name, resolve);
+ } catch(ClassNotFoundException e1) {
+ try {
+ return loadDependencyClass(name, resolve);
+ } catch(ClassNotFoundException e2) {
+ return super.loadClass(name, resolve);
+ }
+ }
+ }
}
- private Class> loadClass0(String name, boolean resolve, boolean checkOther) throws ClassNotFoundException
- {
- try
- {
- return super.loadClass( name, resolve );
- } catch ( ClassNotFoundException ex )
- {
- }
- if ( checkOther )
- {
- for ( PluginClassloader loader : allLoaders )
- {
- if ( loader != this )
- {
- try
- {
- return loader.loadClass0( name, resolve, false );
- } catch ( ClassNotFoundException ex )
- {
- }
- }
+ private Class> loadLocalClass(String name, boolean resolve) throws ClassNotFoundException {
+ Class> cls = findLoadedClass(name);
+ if(cls == null) cls = findClass(name);
+ if(resolve) resolveClass(cls);
+ return cls;
+ }
+
+ private Class> loadDependencyClass(String name, boolean resolve) throws ClassNotFoundException {
+ for(PluginClassloader loader : dependencies) {
+ try {
+ return loader.loadLocalClass(name, resolve);
+ } catch(ClassNotFoundException e) {
+ // continue
}
}
- throw new ClassNotFoundException( name );
+ throw new ClassNotFoundException(name);
}
}
diff --git a/api/src/main/java/net/md_5/bungee/api/plugin/PluginDescription.java b/api/src/main/java/net/md_5/bungee/api/plugin/PluginDescription.java
index ef12ae9037..2568203b4e 100644
--- a/api/src/main/java/net/md_5/bungee/api/plugin/PluginDescription.java
+++ b/api/src/main/java/net/md_5/bungee/api/plugin/PluginDescription.java
@@ -1,8 +1,12 @@
package net.md_5.bungee.api.plugin;
import java.io.File;
+import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
+
+import com.google.common.collect.ImmutableList;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -13,7 +17,7 @@
@Data
@NoArgsConstructor
@AllArgsConstructor
-public class PluginDescription
+public class PluginDescription implements tc.oc.minecraft.api.plugin.PluginDescription
{
/**
@@ -48,4 +52,22 @@ public class PluginDescription
* Optional description.
*/
private String description = null;
+
+ @Override
+ public List getAuthors()
+ {
+ return Collections.singletonList( author );
+ }
+
+ @Override
+ public List getDepend()
+ {
+ return ImmutableList.copyOf( getDepends() );
+ }
+
+ @Override
+ public List getSoftDepend()
+ {
+ return ImmutableList.copyOf( getSoftDepends() );
+ }
}
diff --git a/api/src/main/java/net/md_5/bungee/api/plugin/PluginLogger.java b/api/src/main/java/net/md_5/bungee/api/plugin/PluginLogger.java
index b304ec0d66..8b52dd8d42 100644
--- a/api/src/main/java/net/md_5/bungee/api/plugin/PluginLogger.java
+++ b/api/src/main/java/net/md_5/bungee/api/plugin/PluginLogger.java
@@ -1,10 +1,29 @@
package net.md_5.bungee.api.plugin;
+import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
public class PluginLogger extends Logger
{
+ public static PluginLogger get(Plugin context) {
+ LogManager lm = LogManager.getLogManager();
+ Logger logger = lm.getLogger(context.getClass().getCanonicalName());
+
+ if(logger instanceof PluginLogger) {
+ return (PluginLogger) logger;
+ } else {
+ PluginLogger pluginLogger = new PluginLogger(context);
+
+ // Register the logger under the plugin's name, unless some other logger is already using the name
+ if(logger == null) {
+ lm.addLogger(pluginLogger);
+ pluginLogger.setParent(context.getProxy().getLogger()); // addLogger changes this, change it back
+ }
+
+ return pluginLogger;
+ }
+ }
private final String pluginName;
diff --git a/api/src/main/java/net/md_5/bungee/api/plugin/PluginManager.java b/api/src/main/java/net/md_5/bungee/api/plugin/PluginManager.java
index 71a5a15829..5ced80faf2 100644
--- a/api/src/main/java/net/md_5/bungee/api/plugin/PluginManager.java
+++ b/api/src/main/java/net/md_5/bungee/api/plugin/PluginManager.java
@@ -3,18 +3,16 @@
import com.google.common.base.Preconditions;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
-import com.google.common.eventbus.Subscribe;
import java.io.File;
import java.io.InputStream;
-import java.lang.reflect.Method;
import java.net.URL;
-import java.net.URLClassLoader;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -23,6 +21,8 @@
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.regex.Pattern;
+
+import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
@@ -33,13 +33,14 @@
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.introspector.PropertyUtils;
+import tc.oc.minecraft.api.plugin.PluginFinder;
/**
* Class to manage bridging between plugin duties and implementation duties, for
* example event handling and plugin management.
*/
@RequiredArgsConstructor
-public class PluginManager
+public class PluginManager implements PluginFinder, tc.oc.minecraft.api.event.EventBus
{
private static final Pattern argsSplit = Pattern.compile( " " );
@@ -51,6 +52,9 @@ public class PluginManager
private final Map plugins = new LinkedHashMap<>();
private final Map commandMap = new HashMap<>();
private Map toLoad = new HashMap<>();
+ private final @Getter List loadedPlugins = new ArrayList<>();
+ private final @Getter List enabledPlugins = new ArrayList<>();
+ private final Map classLoaders = new HashMap<>();
private final Multimap commandsByPlugin = ArrayListMultimap.create();
private final Multimap listenersByPlugin = ArrayListMultimap.create();
@@ -174,6 +178,8 @@ public boolean dispatchCommand(CommandSender sender, String commandLine, List getPlugins()
return plugins.values();
}
+ @Override
+ public Collection extends Plugin> getAllPlugins()
+ {
+ return getPlugins();
+ }
+
/**
* Returns a loaded plugin identified by the specified name.
*
@@ -203,15 +215,16 @@ public Plugin getPlugin(String name)
return plugins.get( name );
}
- public void loadPlugins()
+ public void loadPlugins() throws Exception
{
Map pluginStatuses = new HashMap<>();
+ Map pluginLoaders = new HashMap<>();
for ( Map.Entry entry : toLoad.entrySet() )
{
PluginDescription plugin = entry.getValue();
- if ( !enablePlugin( pluginStatuses, new Stack(), plugin ) )
+ if ( !loadPlugin(pluginStatuses, pluginLoaders, new Stack(), plugin ) )
{
- ProxyServer.getInstance().getLogger().log( Level.WARNING, "Failed to enable {0}", entry.getKey() );
+ ProxyServer.getInstance().getLogger().log( Level.SEVERE, "Failed to load {0}", entry.getKey() );
}
}
toLoad.clear();
@@ -220,23 +233,27 @@ public void loadPlugins()
public void enablePlugins()
{
- for ( Plugin plugin : plugins.values() )
+ for ( Plugin plugin : loadedPlugins )
{
try
{
plugin.onEnable();
+ enabledPlugins.add(plugin);
ProxyServer.getInstance().getLogger().log( Level.INFO, "Enabled plugin {0} version {1} by {2}", new Object[]
{
plugin.getDescription().getName(), plugin.getDescription().getVersion(), plugin.getDescription().getAuthor()
} );
} catch ( Throwable t )
{
- ProxyServer.getInstance().getLogger().log( Level.WARNING, "Exception encountered when loading plugin: " + plugin.getDescription().getName(), t );
+ ProxyServer.getInstance().getLogger().log( Level.SEVERE, "Exception encountered when enabling plugin: " + plugin.getDescription().getName(), t );
+ if(proxy.getConfig().isRequireAllPlugins()) {
+ throw t;
+ }
}
}
}
- private boolean enablePlugin(Map pluginStatuses, Stack dependStack, PluginDescription plugin)
+ private boolean loadPlugin(Map pluginStatuses, Map pluginLoaders, Stack dependStack, PluginDescription plugin) throws Exception
{
if ( pluginStatuses.containsKey( plugin ) )
{
@@ -244,12 +261,13 @@ private boolean enablePlugin(Map pluginStatuses, Sta
}
// combine all dependencies for 'for loop'
- Set dependencies = new HashSet<>();
+ Set dependencies = new LinkedHashSet<>();
dependencies.addAll( plugin.getDepends() );
dependencies.addAll( plugin.getSoftDepends() );
// success status
boolean status = true;
+ final List dependLoaders = new ArrayList<>();
// try to load dependencies first
for ( String dependName : dependencies )
@@ -267,19 +285,23 @@ private boolean enablePlugin(Map pluginStatuses, Sta
dependencyGraph.append( element.getName() ).append( " -> " );
}
dependencyGraph.append( plugin.getName() ).append( " -> " ).append( dependName );
- ProxyServer.getInstance().getLogger().log( Level.WARNING, "Circular dependency detected: {0}", dependencyGraph );
+ ProxyServer.getInstance().getLogger().log( Level.SEVERE, "Circular dependency detected: {0}", dependencyGraph );
status = false;
} else
{
dependStack.push( plugin );
- dependStatus = this.enablePlugin( pluginStatuses, dependStack, depend );
+ dependStatus = this.loadPlugin(pluginStatuses, pluginLoaders, dependStack, depend );
dependStack.pop();
}
}
+ if(Boolean.TRUE.equals(dependStatus)) {
+ dependLoaders.add(Preconditions.checkNotNull(pluginLoaders.get(depend)));
+ }
+
if ( dependStatus == Boolean.FALSE && plugin.getDepends().contains( dependName ) ) // only fail if this wasn't a soft dependency
{
- ProxyServer.getInstance().getLogger().log( Level.WARNING, "{0} (required by {1}) is unavailable", new Object[]
+ ProxyServer.getInstance().getLogger().log( Level.SEVERE, "{0} (required by {1}) is unavailable", new Object[]
{
String.valueOf( dependName ), plugin.getName()
} );
@@ -297,26 +319,35 @@ private boolean enablePlugin(Map pluginStatuses, Sta
{
try
{
- URLClassLoader loader = new PluginClassloader( new URL[]
+ PluginClassloader loader = new PluginClassloader(dependLoaders, new URL[]
{
plugin.getFile().toURI().toURL()
} );
+ pluginLoaders.put(plugin, loader);
Class> main = loader.loadClass( plugin.getMain() );
Plugin clazz = (Plugin) main.getDeclaredConstructor().newInstance();
clazz.init( proxy, plugin );
plugins.put( plugin.getName(), clazz );
clazz.onLoad();
+ loadedPlugins.add(clazz);
ProxyServer.getInstance().getLogger().log( Level.INFO, "Loaded plugin {0} version {1} by {2}", new Object[]
{
plugin.getName(), plugin.getVersion(), plugin.getAuthor()
} );
} catch ( Throwable t )
{
- proxy.getLogger().log( Level.WARNING, "Error enabling plugin " + plugin.getName(), t );
+ proxy.getLogger().log( Level.SEVERE, "Error loading plugin " + plugin.getName(), t );
+ if(proxy.getConfig().isRequireAllPlugins()) {
+ throw t;
+ }
}
}
+ if(!status && proxy.getConfig().isRequireAllPlugins()) {
+ throw new IllegalStateException("Failed to load required plugin " + plugin.getName());
+ }
+
pluginStatuses.put( plugin, status );
return status;
}
@@ -388,6 +419,24 @@ public T callEvent(T event)
return event;
}
+ @Override
+ public void registerListener(tc.oc.minecraft.api.plugin.Plugin plugin, tc.oc.minecraft.api.event.Listener listener)
+ {
+ registerListener( (Plugin) plugin, (Listener) listener );
+ }
+
+ @Override
+ public void unregisterListener(tc.oc.minecraft.api.event.Listener listener)
+ {
+ unregisterListener( (Listener) listener );
+ }
+
+ @Override
+ public void unregisterListeners(tc.oc.minecraft.api.plugin.Plugin plugin)
+ {
+ unregisterListeners( (Plugin) plugin );
+ }
+
/**
* Register a {@link Listener} for receiving called events. Methods in this
* Object which wish to receive events must be annotated with the
@@ -398,11 +447,6 @@ public T callEvent(T event)
*/
public void registerListener(Plugin plugin, Listener listener)
{
- for ( Method method : listener.getClass().getDeclaredMethods() )
- {
- Preconditions.checkArgument( !method.isAnnotationPresent( Subscribe.class ),
- "Listener %s has registered using deprecated subscribe annotation! Please update to @EventHandler.", listener );
- }
eventBus.register( listener );
listenersByPlugin.put( plugin, listener );
}
diff --git a/bootstrap/pom.xml b/bootstrap/pom.xml
index 5c42c6fada..c7dd2775be 100644
--- a/bootstrap/pom.xml
+++ b/bootstrap/pom.xml
@@ -4,13 +4,12 @@
4.0.0
- net.md-5
+ tc.oc
bungeecord-parent
1.11-SNAPSHOT
../pom.xml
- net.md-5
bungeecord-bootstrap
1.11-SNAPSHOT
jar
@@ -26,7 +25,7 @@
- net.md-5
+ tc.oc
bungeecord-proxy
${project.version}
compile
diff --git a/bootstrap/src/main/java/net/md_5/bungee/BungeeCordLauncher.java b/bootstrap/src/main/java/net/md_5/bungee/BungeeCordLauncher.java
index 3c1bbe9360..6991d024b6 100644
--- a/bootstrap/src/main/java/net/md_5/bungee/BungeeCordLauncher.java
+++ b/bootstrap/src/main/java/net/md_5/bungee/BungeeCordLauncher.java
@@ -44,15 +44,18 @@ public static void main(String[] args) throws Exception
System.err.println( "*** Warning, this build is outdated ***" );
System.err.println( "*** Please download a new build from http://ci.md-5.net/job/BungeeCord ***" );
System.err.println( "*** You will get NO support regarding this build ***" );
- System.err.println( "*** Server will start in 10 seconds ***" );
- Thread.sleep( TimeUnit.SECONDS.toMillis( 10 ) );
}
}
BungeeCord bungee = new BungeeCord();
ProxyServer.setInstance( bungee );
bungee.getLogger().info( "Enabled BungeeCord version " + bungee.getVersion() );
- bungee.start();
+ try {
+ bungee.start();
+ } catch(Exception e) {
+ bungee.stop("Internal error");
+ throw e;
+ }
if ( !options.has( "noconsole" ) )
{
diff --git a/chat/pom.xml b/chat/pom.xml
index e8f3fd6c59..239c40f753 100644
--- a/chat/pom.xml
+++ b/chat/pom.xml
@@ -4,13 +4,12 @@
4.0.0
- net.md-5
+ tc.oc
bungeecord-parent
1.11-SNAPSHOT
../pom.xml
- net.md-5
bungeecord-chat
1.11-SNAPSHOT
jar
diff --git a/chat/src/main/java/net/md_5/bungee/api/ChatColor.java b/chat/src/main/java/net/md_5/bungee/api/ChatColor.java
index cd88bd4700..e216abf859 100644
--- a/chat/src/main/java/net/md_5/bungee/api/ChatColor.java
+++ b/chat/src/main/java/net/md_5/bungee/api/ChatColor.java
@@ -2,7 +2,10 @@
import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
import java.util.regex.Pattern;
+
+import com.google.common.collect.ImmutableSet;
import lombok.Getter;
/**
@@ -105,6 +108,15 @@ public enum ChatColor
*/
public static final char COLOR_CHAR = '\u00A7';
public static final String ALL_CODES = "0123456789AaBbCcDdEeFfKkLlMmNnOoRr";
+
+ public static final Set DECORATIONS = ImmutableSet.of(
+ BOLD,
+ ITALIC,
+ UNDERLINE,
+ STRIKETHROUGH,
+ MAGIC
+ );
+
/**
* Pattern to remove all colour codes.
*/
@@ -148,6 +160,14 @@ public String toString()
return toString;
}
+ public boolean isColor() {
+ return this == RESET || (code >= '0' && code <= 'f');
+ }
+
+ public boolean isDecoration() {
+ return DECORATIONS.contains(this);
+ }
+
/**
* Strips the given message of all color codes
*
diff --git a/chat/src/main/java/net/md_5/bungee/api/ChatStringBuilder.java b/chat/src/main/java/net/md_5/bungee/api/ChatStringBuilder.java
new file mode 100644
index 0000000000..ac7990b3c8
--- /dev/null
+++ b/chat/src/main/java/net/md_5/bungee/api/ChatStringBuilder.java
@@ -0,0 +1,136 @@
+package net.md_5.bungee.api;
+
+import java.util.EnumSet;
+import java.util.Set;
+import javax.annotation.Nullable;
+
+public class ChatStringBuilder implements Appendable {
+
+ private final StringBuilder builder;
+ private @Nullable ChatColor oldColor, newColor;
+ private final Set oldDecorations, newDecorations;
+ private boolean formatChanged = true;
+ private boolean colored = false;
+
+ public ChatStringBuilder(String initial, @Nullable ChatColor color, Set decorations) {
+ builder = initial != null ? new StringBuilder(initial) : new StringBuilder();
+
+ oldColor = newColor = color;
+
+ oldDecorations = decorations != null ? EnumSet.copyOf(decorations) : EnumSet.noneOf(ChatColor.class);
+ newDecorations = EnumSet.noneOf(ChatColor.class);
+ }
+
+ public ChatStringBuilder() {
+ this(null, null, null);
+ }
+
+ @Override
+ public String toString() {
+ return builder.toString();
+ }
+
+ private void refreshComplete() {
+ if(newColor == null) {
+ newColor = ChatColor.RESET;
+ }
+
+ colored = true;
+ builder.append(oldColor = newColor);
+
+ oldDecorations.clear();
+ for(ChatColor deco : newDecorations) {
+ oldDecorations.add(deco);
+ builder.append(deco);
+ }
+ }
+
+ private void refreshIfChanged() {
+ if(!formatChanged) return;
+
+ // If color changed, a complete refresh is required
+ if(oldColor != newColor) {
+ refreshComplete();
+ return;
+ }
+
+ // If any decorations were removed, a complete refresh is required
+ for(ChatColor deco : oldDecorations) {
+ if(!newDecorations.contains(deco)) {
+ refreshComplete();
+ return;
+ }
+ }
+
+ // If the only change is added decorations, they can just be appended
+ for(ChatColor deco : newDecorations) {
+ if(oldDecorations.add(deco)) {
+ builder.append(deco);
+ }
+ }
+ }
+
+ @Override
+ public ChatStringBuilder append(CharSequence text) {
+ if(text.length() != 0) {
+ refreshIfChanged();
+ builder.append(text);
+ }
+ return this;
+ }
+
+ @Override
+ public ChatStringBuilder append(CharSequence text, int start, int end) {
+ if(start < end) {
+ refreshIfChanged();
+ builder.append(text, start, end);
+ }
+ return this;
+ }
+
+ @Override
+ public Appendable append(char c) {
+ refreshIfChanged();
+ builder.append(c);
+ return this;
+ }
+
+ public void append(Object thing) {
+ append(String.valueOf(thing));
+ }
+
+ public void color(@Nullable ChatColor color) {
+ if(colored && color == null) {
+ // Cannot un-color a legacy string
+ color = ChatColor.RESET;
+ }
+
+ if(newColor != color) {
+ formatChanged = true;
+ newColor = color;
+ }
+ }
+
+ public void decoration(ChatColor decoration, boolean on) {
+ if(on) {
+ if(newDecorations.add(decoration)) {
+ formatChanged = true;
+ }
+ } else {
+ if(newDecorations.remove(decoration)) {
+ formatChanged = true;
+ }
+ }
+ }
+
+ public void decorations(Set decorations) {
+ for(ChatColor deco : ChatColor.DECORATIONS) {
+ decoration(deco, decorations.contains(deco));
+ }
+ }
+
+ public void format(@Nullable ChatColor color, Set decorations) {
+ color(color);
+ decorations(decorations);
+ }
+}
diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java b/chat/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java
index cbb48b082e..15788d0108 100644
--- a/chat/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java
+++ b/chat/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java
@@ -1,23 +1,34 @@
package net.md_5.bungee.api.chat;
-import lombok.AccessLevel;
+import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.ChatStringBuilder;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.EnumSet;
import java.util.List;
-import lombok.ToString;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.Nullable;
@Setter
-@ToString(exclude = "parent")
@NoArgsConstructor
public abstract class BaseComponent
{
-
- @Setter(AccessLevel.NONE)
- BaseComponent parent;
+ /**
+ * An immutable, empty component list. {@link BaseComponent#extra} is always set to this when empty.
+ * Subclasses can use this for their own fields as well.
+ */
+ protected static final List EMPTY_COMPONENT_LIST = Collections.emptyList();
/**
* The color of this component and any child components (unless overridden)
@@ -59,7 +70,7 @@ public abstract class BaseComponent
* Appended components that inherit this component's formatting and events
*/
@Getter
- private List extra;
+ private List extra = EMPTY_COMPONENT_LIST;
/**
* The action to preform when this component (and child components) are
@@ -85,12 +96,9 @@ public abstract class BaseComponent
setInsertion( old.getInsertion() );
setClickEvent( old.getClickEvent() );
setHoverEvent( old.getHoverEvent() );
- if ( old.getExtra() != null )
+ for ( BaseComponent component : old.getExtra() )
{
- for ( BaseComponent component : old.getExtra() )
- {
- addExtra( component.duplicate() );
- }
+ addValidExtra( component.duplicate() );
}
}
@@ -101,6 +109,18 @@ public abstract class BaseComponent
*/
public abstract BaseComponent duplicate();
+ public static String[] toLegacyArray(BaseComponent... components) {
+ final String[] legacies = new String[components.length];
+ for(int i = 0; i < components.length; i++) {
+ legacies[i] = components[i].toLegacyText();
+ }
+ return legacies;
+ }
+
+ public static String toLegacyText(BaseComponent... components) {
+ return toLegacyText(null, ImmutableSet.of(), components);
+ }
+
/**
* Converts the components to a string that uses the old formatting codes
* ({@link net.md_5.bungee.api.ChatColor#COLOR_CHAR}
@@ -108,12 +128,12 @@ public abstract class BaseComponent
* @param components the components to convert
* @return the string in the old format
*/
- public static String toLegacyText(BaseComponent... components)
+ public static String toLegacyText(@Nullable ChatColor color, Set decorations, BaseComponent... components)
{
StringBuilder builder = new StringBuilder();
for ( BaseComponent msg : components )
{
- builder.append( msg.toLegacyText() );
+ builder.append( msg.toLegacyText(color, decorations) );
}
return builder.toString();
}
@@ -135,28 +155,18 @@ public static String toPlainText(BaseComponent... components)
}
/**
- * Returns the color of this component. This uses the parent's color if this
- * component doesn't have one. {@link net.md_5.bungee.api.ChatColor#WHITE}
- * is returned if no color is found.
+ * Returns the color of this component, or {@link net.md_5.bungee.api.ChatColor#WHITE}
+ * if this component has no color.
*
* @return the color of this component
*/
public ChatColor getColor()
{
- if ( color == null )
- {
- if ( parent == null )
- {
- return ChatColor.WHITE;
- }
- return parent.getColor();
- }
- return color;
+ return color != null ? color : ChatColor.WHITE;
}
/**
- * Returns the color of this component without checking the parents color.
- * May return null
+ * Returns the color of this component, or null if this component has no color.
*
* @return the color of this component
*/
@@ -165,6 +175,15 @@ public ChatColor getColorRaw()
return color;
}
+ public ChatColor getColor(ChatColor def) {
+ return color != null ? color : def;
+ }
+
+ public void setColor(@Nullable ChatColor color) {
+ Preconditions.checkArgument(color == null || color.isColor(), "not a color");
+ this.color = color;
+ }
+
/**
* Returns whether this component is bold. This uses the parent's setting if
* this component hasn't been set. false is returned if none of the parent
@@ -174,11 +193,7 @@ public ChatColor getColorRaw()
*/
public boolean isBold()
{
- if ( bold == null )
- {
- return parent != null && parent.isBold();
- }
- return bold;
+ return bold != null && bold;
}
/**
@@ -201,11 +216,7 @@ public Boolean isBoldRaw()
*/
public boolean isItalic()
{
- if ( italic == null )
- {
- return parent != null && parent.isItalic();
- }
- return italic;
+ return italic != null && italic;
}
/**
@@ -228,11 +239,7 @@ public Boolean isItalicRaw()
*/
public boolean isUnderlined()
{
- if ( underlined == null )
- {
- return parent != null && parent.isUnderlined();
- }
- return underlined;
+ return underlined != null && underlined;
}
/**
@@ -255,11 +262,7 @@ public Boolean isUnderlinedRaw()
*/
public boolean isStrikethrough()
{
- if ( strikethrough == null )
- {
- return parent != null && parent.isStrikethrough();
- }
- return strikethrough;
+ return strikethrough != null && strikethrough;
}
/**
@@ -282,11 +285,7 @@ public Boolean isStrikethroughRaw()
*/
public boolean isObfuscated()
{
- if ( obfuscated == null )
- {
- return parent != null && parent.isObfuscated();
- }
- return obfuscated;
+ return obfuscated != null && obfuscated;
}
/**
@@ -300,13 +299,30 @@ public Boolean isObfuscatedRaw()
return obfuscated;
}
+ public void setHoverEvent(HoverEvent hoverEvent) {
+ if(hoverEvent != null) {
+ for(BaseComponent child : hoverEvent.getValue()) validateChild(child);
+ }
+ this.hoverEvent = hoverEvent;
+ }
+
public void setExtra(List components)
{
- for ( BaseComponent component : components )
- {
- component.parent = this;
+ if(components == null || components.isEmpty()) {
+ extra = EMPTY_COMPONENT_LIST;
+ } else {
+ for(BaseComponent child : components) validateChild(child);
+ extra = new ArrayList(components);
+ }
+ }
+
+ public void setExtra(BaseComponent... components) {
+ if(components == null || components.length == 0) {
+ extra = EMPTY_COMPONENT_LIST;
+ } else {
+ for(BaseComponent child : components) validateChild(child);
+ extra = Lists.newArrayList(components);
}
- extra = components;
}
/**
@@ -317,7 +333,7 @@ public void setExtra(List components)
*/
public void addExtra(String text)
{
- addExtra( new TextComponent( text ) );
+ addValidExtra( new TextComponent( text ) );
}
/**
@@ -325,14 +341,24 @@ public void addExtra(String text)
* component's formatting
*
* @param component the component to append
+ * @throws IllegalArgumentException if a component cycle is detected
*/
public void addExtra(BaseComponent component)
{
- if ( extra == null )
+ validateChild( component );
+ addValidExtra(component);
+ }
+
+ /**
+ * Append a component without validating it. This method should only be called when
+ * it is certain not to create a component cycle i.e. when the given component is
+ * known not to contain this one.
+ */
+ private void addValidExtra(BaseComponent component) {
+ if (extra == EMPTY_COMPONENT_LIST)
{
extra = new ArrayList();
}
- component.parent = this;
extra.add( component );
}
@@ -373,26 +399,270 @@ void toPlainText(StringBuilder builder)
}
/**
- * Converts the component to a string that uses the old formatting codes
- * ({@link net.md_5.bungee.api.ChatColor#COLOR_CHAR}
+ * Calls {@link #toLegacyText(ChatColor, Set)} with no default color or decorations.
+ */
+ public String toLegacyText() {
+ return toLegacyText(null, ImmutableSet.of());
+ }
+
+ public String toLegacyText(@Nullable ChatColor color, ChatColor... decorations) {
+ return toLegacyText(color, ImmutableSet.copyOf(decorations));
+ }
+
+ /**
+ * Converts the component to a string that uses the old formatting codes {@link net.md_5.bungee.api.ChatColor#COLOR_CHAR}
+ *
+ * Any given default color or decorations will be applied as if they were inherited from a parent component.
+ * Any part of the string that is not formatted by some component in this tree will have the default formatting
+ * applied.
+ *
+ * If null is given as the default color, then no color is applied to the returned string other than from
+ * the components. This is only possible up to the first formatting applied by a component (because formatting
+ * cannot be "removed" in legacy text, only replaced). Any default-formatted text after that will be given
+ * the {@link ChatColor#RESET} format, which can also be the color passed to this method.
+ *
+ * @param color Default color, or {@link ChatColor#RESET}, or null for no default
+ * @param decorations Default decorations
*
* @return the string in the old format
*/
- public String toLegacyText()
+ public String toLegacyText(@Nullable ChatColor color, Set decorations)
{
- StringBuilder builder = new StringBuilder();
- toLegacyText( builder );
+ Preconditions.checkArgument(color == null || color.isColor(), "default color cannot be a decoration");
+
+ ChatStringBuilder builder = new ChatStringBuilder();
+ toLegacyText(builder, color, decorations);
return builder.toString();
}
- void toLegacyText(StringBuilder builder)
+ protected void toLegacyText(ChatStringBuilder builder, @Nullable ChatColor color, Set decorations)
{
+ color = getColor(color);
+ decorations = getDecorations(decorations);
+
+ toLegacyTextContent(builder, color, decorations);
+
if ( extra != null )
{
for ( BaseComponent e : extra )
{
- e.toLegacyText( builder );
+ e.toLegacyText( builder, color, decorations );
+ }
+ }
+ }
+
+ protected void toLegacyTextContent(ChatStringBuilder builder, @Nullable ChatColor color, Set decorations) {
+ }
+
+ protected static final Joiner JOINER = Joiner.on(", ");
+
+ public Boolean getDecoration(ChatColor decoration) {
+ switch(decoration) {
+ case BOLD: return isBoldRaw();
+ case ITALIC: return isItalicRaw();
+ case UNDERLINE: return isUnderlinedRaw();
+ case STRIKETHROUGH: return isStrikethroughRaw();
+ case MAGIC: return isObfuscatedRaw();
+ default: return null;
+ }
+ }
+
+ public boolean getDecoration(ChatColor decoration, boolean def) {
+ Boolean flag = getDecoration(decoration);
+ if(flag != null) {
+ return flag;
+ } else {
+ return def;
+ }
+ }
+
+ public void setDecoration(ChatColor decoration, Boolean flag) {
+ switch(decoration) {
+ case BOLD: setBold(flag); return;
+ case ITALIC: setItalic(flag); return;
+ case UNDERLINE: setUnderlined(flag); return;
+ case STRIKETHROUGH: setStrikethrough(flag); return;
+ case MAGIC: setObfuscated(flag); return;
+ }
+ }
+
+ public Map getDecorations() {
+ EnumMap decos = new EnumMap(ChatColor.class);
+ for(ChatColor deco : ChatColor.DECORATIONS) {
+ final Boolean flag = getDecoration(deco);
+ if(flag != null) decos.put(deco, flag);
+ }
+ return decos;
+ }
+
+ public Set getDecorations(Set def) {
+ EnumSet decos = EnumSet.noneOf(ChatColor.class);
+ for(ChatColor deco : ChatColor.DECORATIONS) {
+ if(getDecoration(deco, def.contains(deco))) {
+ decos.add(deco);
+ }
+ }
+ return decos;
+ }
+
+ public void mergeDecorations(BaseComponent from) {
+ for(ChatColor deco : ChatColor.DECORATIONS) {
+ Boolean flag = from.getDecoration(deco);
+ if(flag != null) setDecoration(deco, flag);
+ }
+ }
+
+ public void mergeColor(BaseComponent from) {
+ if(from.getColorRaw() != null) {
+ setColor(from.getColorRaw());
+ }
+ }
+
+ public void mergeEvents(BaseComponent from) {
+ if(from.getClickEvent() != null) {
+ setClickEvent(from.getClickEvent());
+ }
+ if(from.getHoverEvent() != null) {
+ setHoverEvent(from.getHoverEvent());
+ }
+ }
+
+ public void mergeFormatting(BaseComponent from) {
+ mergeDecorations(from);
+ mergeColor(from);
+ mergeEvents(from);
+ }
+
+ /**
+ * Verify that the given component can become a part of this component
+ * by checking that they do not already have the inverse relationship.
+ *
+ * This method should be called BEFORE making the given component a child
+ * of this one, so that this component's state remains valid.
+ *
+ * @throws IllegalArgumentException if the given component contains this component
+ */
+ public void validateChild(BaseComponent child) {
+ Preconditions.checkNotNull(child);
+ if(child.contains(this)) {
+ throw new IllegalArgumentException("Component cycle detected between " + this + " and " + child);
+ }
+ }
+
+ /**
+ * Is the given component contained, in whole or in part, by this one?
+ *
+ * This method is used to detect containment cycles. Unlike with collections,
+ * equality is tested with == rather than {@link #equals(Object)}.
+ *
+ * Subclasses with recursive fields of their own should override this method
+ * to include those fields in the search.
+ *
+ * @return true if the given component is the same instance as this component,
+ * or is a child of this component.
+ */
+ public boolean contains(BaseComponent child) {
+ if(this == child) return true;
+
+ for(BaseComponent extra : getExtra()) {
+ if(extra.contains(child)) return true;
+ }
+
+ if(getHoverEvent() != null) {
+ for(BaseComponent value : getHoverEvent().getValue()) {
+ if(value.contains(child)) return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Contribute leading fields to the output of {@link #toString()}.
+ *
+ * These fields should be relatively short i.e. not recursive. They will appear before
+ * any fields contributed by {@link #toStringLast(List)}, which can make the output
+ * easier to read.
+ */
+ protected void toStringFirst(List fields) {
+ if(getColorRaw() != null) {
+ fields.add("color=\"" + getColorRaw().name().toLowerCase() + "\"");
+ }
+
+ for(ChatColor format : ChatColor.DECORATIONS) {
+ final Boolean flag = getDecoration(format);
+ if(flag != null) {
+ fields.add(format.name().toLowerCase() + "=" + flag);
}
}
+
+ if(getClickEvent() != null) {
+ fields.add("clickEvent=" + getClickEvent());
+ }
+ }
+
+ /**
+ * Contribute trailing fields to the output of {@link #toString()}.
+ *
+ * Particularly long fields, i.e. recursive ones, should be added here.
+ */
+ protected void toStringLast(List fields) {
+ if(getHoverEvent() != null) {
+ fields.add("hoverEvent=" + getHoverEvent());
+ }
+
+ if(getExtra() != null && !getExtra().isEmpty()) {
+ fields.add("extra=[" + Joiner.on(", ").join(getExtra()) + "]");
+ }
+ }
+
+ /**
+ * Delegates to {@link #toStringFirst(List)} and {@link #toStringLast(List)}.
+ */
+ @Override
+ public final String toString() {
+ final List fields = new ArrayList();
+ toStringFirst(fields);
+ toStringLast(fields);
+ return getClass().getSimpleName() + "{" + JOINER.join(fields) + "}";
+ }
+
+ /**
+ * {@link #equals(Object)} delegates to this method when its argument is a {@link BaseComponent},
+ * and is not null or this.
+ *
+ * Overrides of this method must call the supermethod in order to compare the base properties.
+ * This should be done after performing any quick comparisons, and before any expensive ones,
+ * as the base method contains both.
+ */
+ protected boolean equals(BaseComponent that) {
+ return Objects.equal(this.color, that.color) &&
+ Objects.equal(this.bold, that.bold) &&
+ Objects.equal(this.italic, that.italic) &&
+ Objects.equal(this.underlined, that.underlined) &&
+ Objects.equal(this.strikethrough, that.strikethrough) &&
+ Objects.equal(this.obfuscated, that.obfuscated) &&
+ Objects.equal(this.insertion, that.insertion) &&
+ Objects.equal(this.clickEvent, that.clickEvent) &&
+ Objects.equal(this.hoverEvent, that.hoverEvent) &&
+ Objects.equal(this.extra, that.extra);
+ }
+
+ /**
+ * Delegates to {@link #equals(BaseComponent)}.
+ */
+ @Override
+ public final boolean equals(Object that) {
+ return that != null && (
+ that == this || (
+ that instanceof BaseComponent &&
+ equals((BaseComponent) that)
+ )
+ );
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(color, bold, italic, underlined, strikethrough, obfuscated, insertion, clickEvent, hoverEvent, extra);
}
}
diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java b/chat/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java
index 1ced284f0e..4ab9f7a38c 100644
--- a/chat/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java
+++ b/chat/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java
@@ -1,5 +1,6 @@
package net.md_5.bungee.api.chat;
+import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
@@ -8,6 +9,7 @@
@Getter
@ToString
@RequiredArgsConstructor
+@EqualsAndHashCode
public final class ClickEvent
{
diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java b/chat/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java
index 1b76956aac..06d0d8dd59 100644
--- a/chat/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java
+++ b/chat/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java
@@ -1,5 +1,6 @@
package net.md_5.bungee.api.chat;
+import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
@@ -7,6 +8,7 @@
@Getter
@ToString
@RequiredArgsConstructor
+@EqualsAndHashCode
final public class HoverEvent
{
diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/ScoreComponent.java b/chat/src/main/java/net/md_5/bungee/api/chat/ScoreComponent.java
new file mode 100644
index 0000000000..f0e9e983c9
--- /dev/null
+++ b/chat/src/main/java/net/md_5/bungee/api/chat/ScoreComponent.java
@@ -0,0 +1,60 @@
+package net.md_5.bungee.api.chat;
+
+import java.util.List;
+import java.util.Set;
+
+import com.google.common.base.Objects;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NonNull;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.ChatStringBuilder;
+
+@Getter
+@AllArgsConstructor
+public class ScoreComponent extends BaseComponent {
+
+ @NonNull private final String name;
+ @NonNull private final String objective;
+
+ @Override
+ public ScoreComponent duplicate() {
+ return new ScoreComponent(getName(), getObjective());
+ }
+
+ private String plainTextContent() {
+ return getName() + ':' + getObjective();
+ }
+
+ @Override
+ void toPlainText(StringBuilder builder) {
+ builder.append(plainTextContent());
+ super.toPlainText(builder);
+ }
+
+ @Override
+ protected void toLegacyTextContent(ChatStringBuilder builder, ChatColor color, Set decorations) {
+ builder.format(color, decorations);
+ builder.append(plainTextContent());
+ }
+
+ @Override
+ protected void toStringFirst(List fields) {
+ fields.add("name=\"" + getName() + '"');
+ fields.add("objective=\"" + getObjective() + '"');
+ super.toStringFirst(fields);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(super.hashCode(), name, objective);
+ }
+
+ @Override
+ protected boolean equals(BaseComponent that) {
+ return that instanceof ScoreComponent &&
+ name.equals(((ScoreComponent) that).getName()) &&
+ objective.equals(((ScoreComponent) that).getObjective()) &&
+ super.equals(that);
+ }
+}
diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/SelectorComponent.java b/chat/src/main/java/net/md_5/bungee/api/chat/SelectorComponent.java
new file mode 100644
index 0000000000..6f35b3e1ab
--- /dev/null
+++ b/chat/src/main/java/net/md_5/bungee/api/chat/SelectorComponent.java
@@ -0,0 +1,53 @@
+package net.md_5.bungee.api.chat;
+
+import java.util.List;
+import java.util.Set;
+
+import com.google.common.base.Objects;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NonNull;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.ChatStringBuilder;
+
+@Getter
+@AllArgsConstructor
+public class SelectorComponent extends BaseComponent {
+
+ @NonNull private final String selector;
+
+ @Override
+ public SelectorComponent duplicate() {
+ return new SelectorComponent(getSelector());
+ }
+
+ @Override
+ void toPlainText(StringBuilder builder) {
+ builder.append(selector);
+ super.toPlainText(builder);
+ }
+
+ @Override
+ protected void toLegacyTextContent(ChatStringBuilder builder, ChatColor color, Set decorations) {
+ builder.format(color, decorations);
+ builder.append(getSelector());
+ }
+
+ @Override
+ protected void toStringFirst(List fields) {
+ fields.add("selector=\"" + getSelector() + '"');
+ super.toStringFirst(fields);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(super.hashCode(), selector);
+ }
+
+ @Override
+ protected boolean equals(BaseComponent that) {
+ return that instanceof SelectorComponent &&
+ selector.equals(((SelectorComponent) that).getSelector()) &&
+ super.equals(that);
+ }
+}
diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/TextComponent.java b/chat/src/main/java/net/md_5/bungee/api/chat/TextComponent.java
index 15a26785e0..7cf4e5b02d 100644
--- a/chat/src/main/java/net/md_5/bungee/api/chat/TextComponent.java
+++ b/chat/src/main/java/net/md_5/bungee/api/chat/TextComponent.java
@@ -1,13 +1,16 @@
package net.md_5.bungee.api.chat;
+import com.google.common.base.Objects;
import lombok.AllArgsConstructor;
import lombok.Getter;
-import lombok.NoArgsConstructor;
+import lombok.NonNull;
import lombok.Setter;
import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.ChatStringBuilder;
import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -19,107 +22,121 @@ public class TextComponent extends BaseComponent
private static final Pattern url = Pattern.compile( "^(?:(https?)://)?([-\\w_\\.]{2,}\\.[a-z]{2,4})(/\\S*)?$" );
+ public static BaseComponent[] fromLegacyArray(String[] legacies, boolean autolink) {
+ final BaseComponent[] components = new BaseComponent[legacies.length];
+ for(int i = 0; i < legacies.length; i++) {
+ components[i] = fromLegacyToComponent(legacies[i], autolink);
+ }
+ return components;
+ }
+
+ /**
+ * Calls {@link #fromLegacyText(String, boolean)} with autolink true
+ */
+ public static BaseComponent[] fromLegacyText(String message) {
+ return fromLegacyText(message, true);
+ }
+
/**
* Converts the old formatting system that used
* {@link net.md_5.bungee.api.ChatColor#COLOR_CHAR} into the new json based
* system.
*
* @param message the text to convert
+ * @param autolink detect links and make them clickable
* @return the components needed to print the message to the client
*/
- public static BaseComponent[] fromLegacyText(String message)
+ public static List fromLegacyToList(String message, boolean autolink)
{
ArrayList components = new ArrayList();
StringBuilder builder = new StringBuilder();
TextComponent component = new TextComponent();
Matcher matcher = url.matcher( message );
+ boolean formatting = false;
for ( int i = 0; i < message.length(); i++ )
{
- char c = message.charAt( i );
- if ( c == ChatColor.COLOR_CHAR )
- {
- i++;
- c = message.charAt( i );
- if ( c >= 'A' && c <= 'Z' )
- {
- c += 32;
- }
- ChatColor format = ChatColor.getByChar( c );
- if ( format == null )
- {
- continue;
- }
- if ( builder.length() > 0 )
- {
- TextComponent old = component;
- component = new TextComponent( old );
- old.setText( builder.toString() );
- builder = new StringBuilder();
- components.add( old );
- }
- switch ( format )
- {
- case BOLD:
- component.setBold( true );
- break;
- case ITALIC:
- component.setItalic( true );
- break;
- case UNDERLINE:
- component.setUnderlined( true );
- break;
- case STRIKETHROUGH:
- component.setStrikethrough( true );
- break;
- case MAGIC:
- component.setObfuscated( true );
- break;
- case RESET:
- format = ChatColor.WHITE;
- default:
- component = new TextComponent();
- component.setColor( format );
- break;
+ final char c = message.charAt( i );
+ if(c == ChatColor.COLOR_CHAR) {
+ formatting = true;
+ } else if(formatting) {
+ formatting = false;
+ final ChatColor format = ChatColor.getByChar(Character.toLowerCase(c));
+ if(format != null) {
+ if(builder.length() > 0) {
+ TextComponent old = component;
+ component = new TextComponent( old );
+ old.setText( builder.toString() );
+ builder = new StringBuilder();
+ components.add( old );
+ }
+
+ switch ( format )
+ {
+ case BOLD:
+ component.setBold( true );
+ break;
+ case ITALIC:
+ component.setItalic( true );
+ break;
+ case UNDERLINE:
+ component.setUnderlined( true );
+ break;
+ case STRIKETHROUGH:
+ component.setStrikethrough( true );
+ break;
+ case MAGIC:
+ component.setObfuscated( true );
+ break;
+ default:
+ component = new TextComponent();
+ component.setColor( format );
+ break;
+ }
}
- continue;
- }
- int pos = message.indexOf( ' ', i );
- if ( pos == -1 )
- {
- pos = message.length();
- }
- if ( matcher.region( i, pos ).find() )
- { //Web link handling
-
- if ( builder.length() > 0 )
- {
- TextComponent old = component;
- component = new TextComponent( old );
- old.setText( builder.toString() );
- builder = new StringBuilder();
- components.add( old );
+ } else {
+ if(autolink) {
+ int pos = message.indexOf(' ', i);
+ if(pos == -1) {
+ pos = message.length();
+ }
+
+ if(matcher.region(i, pos).find()) { //Web link handling
+ if(builder.length() > 0) {
+ TextComponent old = component;
+ component = new TextComponent(old);
+ old.setText(builder.toString());
+ builder = new StringBuilder();
+ components.add(old);
+ }
+
+ final String urlString = message.substring(i, pos);
+ final TextComponent urlComponent = new TextComponent(component);
+ urlComponent.setText(urlString);
+ urlComponent.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL,
+ urlString.startsWith("http") ? urlString : "http://" + urlString));
+ components.add(urlComponent);
+ i = pos - 1;
+ continue;
+ }
}
- TextComponent old = component;
- component = new TextComponent( old );
- String urlString = message.substring( i, pos );
- component.setText( urlString );
- component.setClickEvent( new ClickEvent( ClickEvent.Action.OPEN_URL,
- urlString.startsWith( "http" ) ? urlString : "http://" + urlString ) );
- components.add( component );
- i += pos - i - 1;
- component = old;
- continue;
+ builder.append( c );
}
- builder.append( c );
}
+
if ( builder.length() > 0 )
{
component.setText( builder.toString() );
components.add( component );
}
+ return components;
+ }
+
+ public static BaseComponent[] fromLegacyText(String message, boolean autolink) {
+ final List components = fromLegacyToList(message, autolink);
+
// The client will crash if the array is empty
if ( components.isEmpty() )
{
@@ -129,17 +146,25 @@ public static BaseComponent[] fromLegacyText(String message)
return components.toArray( new BaseComponent[ components.size() ] );
}
+ public static BaseComponent fromLegacyToComponent(String message, boolean autolink) {
+ final List components = fromLegacyToList(message, autolink);
+ switch(components.size()) {
+ case 0: return new TextComponent();
+ case 1: return components.get(0);
+ default: return new TextComponent(components);
+ }
+ }
+
/**
* The text of the component that will be displayed to the client
*/
- private String text;
+ @NonNull private String text;
/**
- * Creates a TextComponent with blank text.
+ * Creates a blank component
*/
- public TextComponent()
- {
- this.text = "";
+ public TextComponent() {
+ this("");
}
/**
@@ -163,7 +188,16 @@ public TextComponent(TextComponent textComponent)
public TextComponent(BaseComponent... extras)
{
setText( "" );
- setExtra( new ArrayList( Arrays.asList( extras ) ) );
+ setExtra( extras );
+ }
+
+ public TextComponent(List extra) {
+ this("", extra);
+ }
+
+ public TextComponent(String text, List extra) {
+ setText(text);
+ setExtra(extra);
}
/**
@@ -185,36 +219,26 @@ protected void toPlainText(StringBuilder builder)
}
@Override
- protected void toLegacyText(StringBuilder builder)
+ protected void toLegacyTextContent(ChatStringBuilder builder, ChatColor color, Set decorations)
{
- builder.append( getColor() );
- if ( isBold() )
- {
- builder.append( ChatColor.BOLD );
- }
- if ( isItalic() )
- {
- builder.append( ChatColor.ITALIC );
- }
- if ( isUnderlined() )
- {
- builder.append( ChatColor.UNDERLINE );
- }
- if ( isStrikethrough() )
- {
- builder.append( ChatColor.STRIKETHROUGH );
- }
- if ( isObfuscated() )
- {
- builder.append( ChatColor.MAGIC );
- }
+ builder.format(color, decorations);
builder.append( text );
- super.toLegacyText( builder );
+ }
+
+ @Override protected void toStringFirst(List fields) {
+ fields.add("text=\"" + getText() + "\"");
+ super.toStringFirst(fields);
}
@Override
- public String toString()
- {
- return String.format( "TextComponent{text=%s, %s}", text, super.toString() );
+ public int hashCode() {
+ return Objects.hashCode(super.hashCode(), text);
+ }
+
+ @Override
+ protected boolean equals(BaseComponent that) {
+ return that instanceof TextComponent &&
+ text.equals(((TextComponent) that).getText()) &&
+ super.equals(that);
}
}
diff --git a/chat/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java b/chat/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java
index 79c2e32e65..34d2ed167d 100644
--- a/chat/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java
+++ b/chat/src/main/java/net/md_5/bungee/api/chat/TranslatableComponent.java
@@ -1,36 +1,41 @@
package net.md_5.bungee.api.chat;
+import com.google.common.base.Objects;
+import com.google.common.collect.Lists;
import lombok.Getter;
-import lombok.NoArgsConstructor;
+import lombok.NonNull;
import lombok.Setter;
-import net.md_5.bungee.api.ChatColor;
+
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import lombok.ToString;
+
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.ChatStringBuilder;
@Getter
@Setter
-@ToString
-@NoArgsConstructor
public class TranslatableComponent extends BaseComponent
{
- private final ResourceBundle locales = ResourceBundle.getBundle( "mojang-translations/en_US" );
- private final Pattern format = Pattern.compile( "%(?:(\\d+)\\$)?([A-Za-z%]|$)" );
+ private static final ResourceBundle locales = ResourceBundle.getBundle( "mojang-translations/en_US" );
+ private static final Pattern format = Pattern.compile( "%(?:(\\d+)\\$)?([A-Za-z%]|$)" );
/**
* The key into the Minecraft locale files to use for the translation. The
* text depends on the client's locale setting. The console is always en_US
*/
- private String translate;
+ @NonNull private String translate;
+
/**
* The components to substitute into the translation
*/
- private List with;
+ private List with = EMPTY_COMPONENT_LIST;
/**
* Creates a translatable component from the original to clone it.
@@ -49,7 +54,7 @@ public TranslatableComponent(TranslatableComponent original)
{
temp.add( baseComponent.duplicate() );
}
- setWith( temp );
+ setWithInternal( temp );
}
}
@@ -77,7 +82,7 @@ public TranslatableComponent(String translate, Object... with)
temp.add( (BaseComponent) w );
}
}
- setWith( temp );
+ setWithInternal( temp );
}
/**
@@ -99,11 +104,27 @@ public BaseComponent duplicate()
*/
public void setWith(List components)
{
- for ( BaseComponent component : components )
- {
- component.parent = this;
+ if(components == null) {
+ setWithInternal(null);
+ } else {
+ for(BaseComponent child : components) validateChild(child);
+ setWithInternal(new ArrayList(components));
}
- with = components;
+ }
+
+ /**
+ * Sets the translation substitutions to be used in this component. Removes
+ * any previously set substitutions
+ *
+ * @param components the components to substitute
+ */
+ public void setWith(BaseComponent... components)
+ {
+ setWith(components == null ? EMPTY_COMPONENT_LIST : Arrays.asList(components));
+ }
+
+ private void setWithInternal(List components) {
+ with = components == null || components.isEmpty() ? EMPTY_COMPONENT_LIST : components;
}
/**
@@ -125,11 +146,11 @@ public void addWith(String text)
*/
public void addWith(BaseComponent component)
{
- if ( with == null )
+ validateChild(component);
+ if ( with == EMPTY_COMPONENT_LIST )
{
with = new ArrayList();
}
- component.parent = this;
with.add( component );
}
@@ -179,7 +200,7 @@ protected void toPlainText(StringBuilder builder)
}
@Override
- protected void toLegacyText(StringBuilder builder)
+ protected void toLegacyTextContent(ChatStringBuilder builder, ChatColor color, Set decorations)
{
String trans;
try
@@ -198,7 +219,7 @@ protected void toLegacyText(StringBuilder builder)
int pos = matcher.start();
if ( pos != position )
{
- addFormat( builder );
+ builder.format( color, decorations );
builder.append( trans.substring( position, pos ) );
}
position = matcher.end();
@@ -209,44 +230,54 @@ protected void toLegacyText(StringBuilder builder)
case 's':
case 'd':
String withIndex = matcher.group( 1 );
- with.get( withIndex != null ? Integer.parseInt( withIndex ) - 1 : i++ ).toLegacyText( builder );
+ with.get( withIndex != null ? Integer.parseInt( withIndex ) - 1 : i++ ).toLegacyText( builder, color, decorations );
break;
case '%':
- addFormat( builder );
+ builder.format( color, decorations );
builder.append( '%' );
break;
}
}
if ( trans.length() != position )
{
- addFormat( builder );
+ builder.format( color, decorations );
builder.append( trans.substring( position, trans.length() ) );
}
- super.toLegacyText( builder );
}
- private void addFormat(StringBuilder builder)
- {
- builder.append( getColor() );
- if ( isBold() )
- {
- builder.append( ChatColor.BOLD );
- }
- if ( isItalic() )
- {
- builder.append( ChatColor.ITALIC );
- }
- if ( isUnderlined() )
- {
- builder.append( ChatColor.UNDERLINE );
- }
- if ( isStrikethrough() )
- {
- builder.append( ChatColor.STRIKETHROUGH );
+ @Override
+ public boolean contains(BaseComponent child) {
+ if(super.contains(child)) return true;
+ for(BaseComponent with : getWith()) {
+ if(with.contains(child)) return true;
}
- if ( isObfuscated() )
- {
- builder.append( ChatColor.MAGIC );
+ return false;
+ }
+
+ @Override
+ protected void toStringFirst(List fields) {
+ fields.add("translate=\"" + getTranslate() + "\"");
+ super.toStringFirst(fields);
+ }
+
+ @Override
+ protected void toStringLast(List fields) {
+ if(getWith() != null && !getWith().isEmpty()) {
+ fields.add("with=[" + JOINER.join(getWith()) + "]");
}
+ super.toStringLast(fields);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(super.hashCode(), translate, with);
+ }
+
+ @Override
+ protected boolean equals(BaseComponent that) {
+ return that instanceof TranslatableComponent &&
+ translate.equals(((TranslatableComponent) that).getTranslate()) &&
+ super.equals(that) &&
+ with.equals(((TranslatableComponent) that).getWith());
}
}
diff --git a/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java
index 9aed9c564b..284490155e 100644
--- a/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java
+++ b/chat/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java
@@ -1,6 +1,5 @@
package net.md_5.bungee.chat;
-import com.google.common.base.Preconditions;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
@@ -10,7 +9,6 @@
import net.md_5.bungee.api.chat.HoverEvent;
import java.util.Arrays;
-import java.util.HashSet;
public class BaseComponentSerializer
{
@@ -78,72 +76,54 @@ protected void deserialize(JsonObject object, BaseComponent component, JsonDeser
protected void serialize(JsonObject object, BaseComponent component, JsonSerializationContext context)
{
- boolean first = false;
- if ( ComponentSerializer.serializedComponents.get() == null )
+ if ( component.getColorRaw() != null )
{
- first = true;
- ComponentSerializer.serializedComponents.set( new HashSet() );
+ object.addProperty( "color", component.getColorRaw().getName() );
}
- try
+ if ( component.isBoldRaw() != null )
{
- Preconditions.checkArgument( !ComponentSerializer.serializedComponents.get().contains( component ), "Component loop" );
- ComponentSerializer.serializedComponents.get().add( component );
- if ( component.getColorRaw() != null )
- {
- object.addProperty( "color", component.getColorRaw().getName() );
- }
- if ( component.isBoldRaw() != null )
- {
- object.addProperty( "bold", component.isBoldRaw() );
- }
- if ( component.isItalicRaw() != null )
- {
- object.addProperty( "italic", component.isItalicRaw() );
- }
- if ( component.isUnderlinedRaw() != null )
- {
- object.addProperty( "underlined", component.isUnderlinedRaw() );
- }
- if ( component.isStrikethroughRaw() != null )
- {
- object.addProperty( "strikethrough", component.isStrikethroughRaw() );
- }
- if ( component.isObfuscatedRaw() != null )
- {
- object.addProperty( "obfuscated", component.isObfuscatedRaw() );
- }
- if ( component.getInsertion() != null )
- {
- object.addProperty( "insertion", component.getInsertion() );
- }
+ object.addProperty( "bold", component.isBoldRaw() );
+ }
+ if ( component.isItalicRaw() != null )
+ {
+ object.addProperty( "italic", component.isItalicRaw() );
+ }
+ if ( component.isUnderlinedRaw() != null )
+ {
+ object.addProperty( "underlined", component.isUnderlinedRaw() );
+ }
+ if ( component.isStrikethroughRaw() != null )
+ {
+ object.addProperty( "strikethrough", component.isStrikethroughRaw() );
+ }
+ if ( component.isObfuscatedRaw() != null )
+ {
+ object.addProperty( "obfuscated", component.isObfuscatedRaw() );
+ }
+ if ( component.getInsertion() != null )
+ {
+ object.addProperty( "insertion", component.getInsertion() );
+ }
- if ( component.getExtra() != null )
- {
- object.add( "extra", context.serialize( component.getExtra() ) );
- }
+ if ( component.getExtra() != null && !component.getExtra().isEmpty() )
+ {
+ object.add( "extra", context.serialize( component.getExtra() ) );
+ }
- //Events
- if ( component.getClickEvent() != null )
- {
- JsonObject clickEvent = new JsonObject();
- clickEvent.addProperty( "action", component.getClickEvent().getAction().toString().toLowerCase() );
- clickEvent.addProperty( "value", component.getClickEvent().getValue() );
- object.add( "clickEvent", clickEvent );
- }
- if ( component.getHoverEvent() != null )
- {
- JsonObject hoverEvent = new JsonObject();
- hoverEvent.addProperty( "action", component.getHoverEvent().getAction().toString().toLowerCase() );
- hoverEvent.add( "value", context.serialize( component.getHoverEvent().getValue() ) );
- object.add( "hoverEvent", hoverEvent );
- }
- } finally
+ //Events
+ if ( component.getClickEvent() != null )
{
- ComponentSerializer.serializedComponents.get().remove( component );
- if ( first )
- {
- ComponentSerializer.serializedComponents.set( null );
- }
+ JsonObject clickEvent = new JsonObject();
+ clickEvent.addProperty( "action", component.getClickEvent().getAction().toString().toLowerCase() );
+ clickEvent.addProperty( "value", component.getClickEvent().getValue() );
+ object.add( "clickEvent", clickEvent );
+ }
+ if ( component.getHoverEvent() != null )
+ {
+ JsonObject hoverEvent = new JsonObject();
+ hoverEvent.addProperty( "action", component.getHoverEvent().getAction().toString().toLowerCase() );
+ hoverEvent.add( "value", context.serialize( component.getHoverEvent().getValue() ) );
+ object.add( "hoverEvent", hoverEvent );
}
}
}
diff --git a/chat/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java
index 5756fc4afd..b9d795d2a5 100644
--- a/chat/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java
+++ b/chat/src/main/java/net/md_5/bungee/chat/ComponentSerializer.java
@@ -8,23 +8,24 @@
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import net.md_5.bungee.api.chat.BaseComponent;
+import net.md_5.bungee.api.chat.ScoreComponent;
+import net.md_5.bungee.api.chat.SelectorComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.chat.TranslatableComponent;
import java.lang.reflect.Type;
-import java.util.HashSet;
public class ComponentSerializer implements JsonDeserializer
{
private final static Gson gson = new GsonBuilder().
- registerTypeAdapter( BaseComponent.class, new ComponentSerializer() ).
- registerTypeAdapter( TextComponent.class, new TextComponentSerializer() ).
- registerTypeAdapter( TranslatableComponent.class, new TranslatableComponentSerializer() ).
+ registerTypeHierarchyAdapter( BaseComponent.class, new ComponentSerializer() ).
+ registerTypeHierarchyAdapter( TextComponent.class, new TextComponentSerializer() ).
+ registerTypeHierarchyAdapter( TranslatableComponent.class, new TranslatableComponentSerializer() ).
+ registerTypeHierarchyAdapter( SelectorComponent.class, new SelectorComponentSerializer() ).
+ registerTypeHierarchyAdapter( ScoreComponent.class, new ScoreComponentSerializer() ).
create();
- public final static ThreadLocal> serializedComponents = new ThreadLocal>();
-
public static BaseComponent[] parse(String json)
{
if ( json.startsWith( "[" ) )
@@ -59,6 +60,14 @@ public BaseComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializa
{
return context.deserialize( json, TranslatableComponent.class );
}
+ if ( object.has( "selector" ) )
+ {
+ return context.deserialize( json, SelectorComponent.class );
+ }
+ if ( object.has( "score" ) )
+ {
+ return context.deserialize( json, ScoreComponent.class );
+ }
return context.deserialize( json, TextComponent.class );
}
}
diff --git a/chat/src/main/java/net/md_5/bungee/chat/ScoreComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/ScoreComponentSerializer.java
new file mode 100644
index 0000000000..07f3576d11
--- /dev/null
+++ b/chat/src/main/java/net/md_5/bungee/chat/ScoreComponentSerializer.java
@@ -0,0 +1,38 @@
+package net.md_5.bungee.chat;
+
+import java.lang.reflect.Type;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import net.md_5.bungee.api.chat.ScoreComponent;
+
+public class ScoreComponentSerializer extends BaseComponentSerializer implements JsonSerializer, JsonDeserializer {
+
+ @Override
+ public ScoreComponent deserialize(JsonElement jsonElement, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+ final JsonObject json = jsonElement.getAsJsonObject();
+ final JsonObject score = json.getAsJsonObject("score");
+ final ScoreComponent component = new ScoreComponent(score.get("name").getAsString(),
+ score.get("objective").getAsString());
+ deserialize(json, component, context);
+ return component;
+ }
+
+ @Override
+ public JsonElement serialize(ScoreComponent src, Type typeOfSrc, JsonSerializationContext context) {
+ final JsonObject score = new JsonObject();
+ score.addProperty("name", src.getName());
+ score.addProperty("objective", src.getObjective());
+
+ final JsonObject json = new JsonObject();
+ serialize(json, src, context);
+ json.add("score", score);
+
+ return json;
+ }
+}
diff --git a/chat/src/main/java/net/md_5/bungee/chat/SelectorComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/SelectorComponentSerializer.java
new file mode 100644
index 0000000000..c5fbf641a5
--- /dev/null
+++ b/chat/src/main/java/net/md_5/bungee/chat/SelectorComponentSerializer.java
@@ -0,0 +1,31 @@
+package net.md_5.bungee.chat;
+
+import java.lang.reflect.Type;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import net.md_5.bungee.api.chat.SelectorComponent;
+
+public class SelectorComponentSerializer extends BaseComponentSerializer implements JsonSerializer, JsonDeserializer {
+
+ @Override
+ public SelectorComponent deserialize(JsonElement jsonElement, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+ final JsonObject json = jsonElement.getAsJsonObject();
+ final SelectorComponent component = new SelectorComponent(json.get("selector").getAsString());
+ deserialize(json, component, context);
+ return component;
+ }
+
+ @Override
+ public JsonElement serialize(SelectorComponent src, Type typeOfSrc, JsonSerializationContext context) {
+ final JsonObject json = new JsonObject();
+ serialize(json, src, context);
+ json.addProperty("selector", src.getSelector());
+ return json;
+ }
+}
diff --git a/chat/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java b/chat/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java
index c01af26864..a6019d6211 100644
--- a/chat/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java
+++ b/chat/src/main/java/net/md_5/bungee/chat/TranslatableComponentSerializer.java
@@ -19,10 +19,9 @@ public class TranslatableComponentSerializer extends BaseComponentSerializer imp
@Override
public TranslatableComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{
- TranslatableComponent component = new TranslatableComponent();
JsonObject object = json.getAsJsonObject();
+ TranslatableComponent component = new TranslatableComponent(object.get( "translate" ).getAsString());
deserialize( object, component, context );
- component.setTranslate( object.get( "translate" ).getAsString() );
if ( object.has( "with" ) )
{
component.setWith( Arrays.asList( (BaseComponent[]) context.deserialize( object.get( "with" ), BaseComponent[].class ) ) );
diff --git a/chat/src/test/java/net/md_5/bungee/api/chat/TextComponentTest.java b/chat/src/test/java/net/md_5/bungee/api/chat/TextComponentTest.java
new file mode 100644
index 0000000000..93f93a69ca
--- /dev/null
+++ b/chat/src/test/java/net/md_5/bungee/api/chat/TextComponentTest.java
@@ -0,0 +1,152 @@
+package net.md_5.bungee.api.chat;
+
+import net.md_5.bungee.api.ChatColor;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class TextComponentTest {
+
+ @Test
+ public void fromLegacyNoFormatting() throws Exception {
+ assertEquals(new TextComponent("woot"),
+ TextComponent.fromLegacyToComponent("woot", false));
+ }
+
+ @Test
+ public void fromLegacyWithFormatting() throws Exception {
+ TextComponent c = new TextComponent("woot");
+ c.setColor(ChatColor.GREEN);
+ c.setBold(true);
+
+ assertEquals(c, TextComponent.fromLegacyToComponent(ChatColor.GREEN.toString() + ChatColor.BOLD + "woot", false));
+ }
+
+ @Test
+ public void fromLegacyPartialColor() throws Exception {
+ TextComponent one = new TextComponent("One");
+ TextComponent two = new TextComponent("Two"); two.setColor(ChatColor.GREEN);
+ TextComponent c = new TextComponent(one, two);
+
+ assertEquals(c, TextComponent.fromLegacyToComponent("One" + ChatColor.GREEN + "Two", false));
+ }
+
+ @Test
+ public void fromLegacyAutoLinkStandalone() throws Exception {
+ TextComponent c = new TextComponent("https://oc.tc");
+ c.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://oc.tc"));
+
+ assertEquals(c, TextComponent.fromLegacyToComponent("https://oc.tc", true));
+ }
+
+ @Test
+ public void fromLegacyAutoLinkInline() throws Exception {
+ TextComponent one = new TextComponent("Go to ");
+ TextComponent two = new TextComponent("https://oc.tc"); two.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://oc.tc"));
+ TextComponent three = new TextComponent(" for a good time");
+ TextComponent c = new TextComponent(one, two, three);
+
+ assertEquals(c, TextComponent.fromLegacyToComponent("Go to https://oc.tc for a good time", true));
+ }
+
+ @Test
+ public void toLegacyNoFormatting() throws Exception {
+ assertEquals("woot", new TextComponent("woot").toLegacyText());
+ }
+
+ @Test
+ public void toLegacyWithFormatting() throws Exception {
+ TextComponent c = new TextComponent("woot");
+ c.setColor(ChatColor.GREEN);
+ c.setBold(true);
+
+ assertEquals(ChatColor.GREEN.toString() + ChatColor.BOLD + "woot",
+ c.toLegacyText());
+ }
+
+ @Test
+ public void toLegacyPartialColorNoDefault() throws Exception {
+ TextComponent one = new TextComponent("One");
+ TextComponent two = new TextComponent("Two"); two.setColor(ChatColor.GREEN);
+ TextComponent three = new TextComponent("Three");
+ one.addExtra(two);
+ one.addExtra(three);
+
+ assertEquals("One" + ChatColor.GREEN + "Two" + ChatColor.RESET + "Three",
+ one.toLegacyText());
+ }
+
+ @Test
+ public void toLegacyPartialColorWithDefault() throws Exception {
+ TextComponent one = new TextComponent("One");
+ TextComponent two = new TextComponent("Two"); two.setColor(ChatColor.GREEN);
+ TextComponent three = new TextComponent("Three");
+ one.addExtra(two);
+ one.addExtra(three);
+
+ assertEquals(ChatColor.RED + "One" + ChatColor.GREEN + "Two" + ChatColor.RED + "Three",
+ one.toLegacyText(ChatColor.RED));
+ }
+
+ @Test
+ public void toLegacyAddDecoration() throws Exception {
+ TextComponent one = new TextComponent("One"); one.setColor(ChatColor.RED);
+ TextComponent two = new TextComponent("Two"); two.setBold(true);
+ one.addExtra(two);
+
+ assertEquals(ChatColor.RED + "One" + ChatColor.BOLD + "Two",
+ one.toLegacyText());
+ }
+
+ @Test
+ public void toLegacyRemoveDecoration() throws Exception {
+ TextComponent one = new TextComponent("One"); one.setColor(ChatColor.RED);
+ TextComponent two = new TextComponent("Two"); two.setBold(true);
+ TextComponent three = new TextComponent("Three");
+ one.addExtra(two);
+ one.addExtra(three);
+
+ assertEquals(ChatColor.RED + "One" + ChatColor.BOLD + "Two" + ChatColor.RED + "Three",
+ one.toLegacyText());
+ }
+
+ @Test
+ public void toLegacyNestedDecoration() throws Exception {
+ TextComponent one = new TextComponent("One"); one.setColor(ChatColor.RED);
+ TextComponent two = new TextComponent("Two"); two.setBold(true);
+ TextComponent three = new TextComponent("Three"); three.setItalic(true);
+ one.addExtra(two);
+ two.addExtra(three);
+ two.addExtra(new TextComponent("Two"));
+ one.addExtra(new TextComponent("One"));
+
+ assertEquals(ChatColor.RED + "One" + ChatColor.BOLD + "Two" + ChatColor.ITALIC + "Three" + ChatColor.RED + ChatColor.BOLD + "Two" + ChatColor.RED + "One",
+ one.toLegacyText());
+ }
+
+ @Test
+ public void toLegacyCollapseFormatting() throws Exception {
+ TextComponent one = new TextComponent(""); one.setColor(ChatColor.RED);
+ TextComponent two = new TextComponent(""); two.setColor(ChatColor.GREEN);
+ TextComponent three = new TextComponent("Hi"); three.setColor(ChatColor.BLUE);
+ TextComponent four = new TextComponent(""); four.setColor(ChatColor.YELLOW);
+ TextComponent five = new TextComponent("There"); five.setColor(ChatColor.BLUE);
+ one.addExtra(two);
+ one.addExtra(three);
+ one.addExtra(four);
+ one.addExtra(five);
+
+ assertEquals(ChatColor.BLUE + "HiThere",
+ one.toLegacyText());
+ }
+
+ @Test
+ public void toLegacyWithClickEvent() throws Exception {
+ TextComponent one = new TextComponent("Go to ");
+ TextComponent two = new TextComponent("https://oc.tc"); two.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://oc.tc"));
+ TextComponent three = new TextComponent(" for a good time");
+ TextComponent c = new TextComponent(one, two, three);
+
+ assertEquals("Go to https://oc.tc for a good time", c.toLegacyText());
+ }
+}
diff --git a/chat/src/test/java/net/md_5/bungee/api/chat/TranslatableComponentTest.java b/chat/src/test/java/net/md_5/bungee/api/chat/TranslatableComponentTest.java
index 453d375d15..70be2889c6 100644
--- a/chat/src/test/java/net/md_5/bungee/api/chat/TranslatableComponentTest.java
+++ b/chat/src/test/java/net/md_5/bungee/api/chat/TranslatableComponentTest.java
@@ -13,6 +13,6 @@ 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() );
+ assertEquals( "Test string with 2 placeholders: aoeu", testComponent.toLegacyText() );
}
}
diff --git a/config/pom.xml b/config/pom.xml
index 5d32d0e842..e731d711e5 100644
--- a/config/pom.xml
+++ b/config/pom.xml
@@ -4,13 +4,12 @@
4.0.0
- net.md-5
+ tc.oc
bungeecord-parent
1.11-SNAPSHOT
../pom.xml
- net.md-5
bungeecord-config
1.11-SNAPSHOT
jar
@@ -19,6 +18,12 @@
Generic java configuration API intended for use with BungeeCord
+
+ tc.oc
+ minecraft-api
+ 1.11-SNAPSHOT
+ compile
+
org.yaml
snakeyaml
diff --git a/config/src/main/java/net/md_5/bungee/config/Configuration.java b/config/src/main/java/net/md_5/bungee/config/Configuration.java
index 967a1b2acd..38e2d6a17a 100644
--- a/config/src/main/java/net/md_5/bungee/config/Configuration.java
+++ b/config/src/main/java/net/md_5/bungee/config/Configuration.java
@@ -1,5 +1,6 @@
package net.md_5.bungee.config;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collection;
@@ -7,12 +8,17 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import lombok.Getter;
+import tc.oc.minecraft.api.configuration.AbstractConfigurationSection;
+import tc.oc.minecraft.api.configuration.InvalidConfigurationException;
-public final class Configuration
+public final class Configuration extends AbstractConfigurationSection implements tc.oc.minecraft.api.configuration.Configuration
{
private static final char SEPARATOR = '.';
+ final String absolutePath;
final Map self;
+ @Getter
private final Configuration defaults;
public Configuration()
@@ -25,8 +31,19 @@ public Configuration(Configuration defaults)
this( new LinkedHashMap(), defaults );
}
+ public Configuration(Configuration values, Configuration defaults)
+ {
+ this( values.self, defaults );
+ }
+
Configuration(Map, ?> map, Configuration defaults)
{
+ this("", map, defaults);
+ }
+
+ Configuration(String absolutePath, Map, ?> map, Configuration defaults)
+ {
+ this.absolutePath = absolutePath;
this.self = new LinkedHashMap<>();
this.defaults = defaults;
@@ -56,7 +73,7 @@ private Configuration getSectionFor(String path)
Object section = self.get( root );
if ( section == null )
{
- section = new Configuration( ( defaults == null ) ? null : defaults.getSection( path ) );
+ section = new Configuration( resolvePath(path), ImmutableMap.of(), (defaults == null ) ? null : defaults.getSection(path ) );
self.put( root, section );
}
@@ -136,6 +153,11 @@ public Configuration getSection(String path)
return (Configuration) get( path, ( def instanceof Configuration ) ? def : new Configuration( ( defaults == null ) ? null : defaults.getSection( path ) ) );
}
+ @Override
+ public Configuration needSection(String path) throws InvalidConfigurationException {
+ return needType(path, Configuration.class);
+ }
+
/**
* Gets keys, not deep by default.
*
@@ -411,4 +433,9 @@ public List> getList(String path, List> def)
Object val = get( path, def );
return ( val instanceof List> ) ? (List>) val : def;
}
+
+ @Override
+ public String getCurrentPath() {
+ return absolutePath;
+ }
}
diff --git a/event/pom.xml b/event/pom.xml
index 65a642244e..bf8b306550 100644
--- a/event/pom.xml
+++ b/event/pom.xml
@@ -4,13 +4,12 @@
4.0.0
- net.md-5
+ tc.oc
bungeecord-parent
1.11-SNAPSHOT
../pom.xml
- net.md-5
bungeecord-event
1.11-SNAPSHOT
jar
diff --git a/event/src/main/java/net/md_5/bungee/event/EventBus.java b/event/src/main/java/net/md_5/bungee/event/EventBus.java
index 5b5d42010b..080349d2b9 100644
--- a/event/src/main/java/net/md_5/bungee/event/EventBus.java
+++ b/event/src/main/java/net/md_5/bungee/event/EventBus.java
@@ -52,7 +52,7 @@ public void post(Object event)
throw new Error( "Method rejected target/argument: " + event, ex );
} catch ( InvocationTargetException ex )
{
- logger.log( Level.WARNING, MessageFormat.format( "Error dispatching event {0} to listener {1}", event, method.getListener() ), ex.getCause() );
+ logger.log( Level.SEVERE, MessageFormat.format( "Error dispatching event {0} to listener {1}", event, method.getListener() ), ex.getCause() );
}
}
}
diff --git a/log/pom.xml b/log/pom.xml
index 22a933f273..56528a4bc9 100644
--- a/log/pom.xml
+++ b/log/pom.xml
@@ -4,13 +4,13 @@
4.0.0
- net.md-5
+ tc.oc
bungeecord-parent
1.11-SNAPSHOT
../pom.xml
- net.md-5
+ tc.oc
bungeecord-log
1.11-SNAPSHOT
jar
@@ -26,7 +26,7 @@
compile
- net.md-5
+ tc.oc
bungeecord-chat
${project.version}
compile
diff --git a/log/src/main/java/net/md_5/bungee/log/BungeeLogger.java b/log/src/main/java/net/md_5/bungee/log/BungeeLogger.java
index 9598fff324..ea042038c5 100644
--- a/log/src/main/java/net/md_5/bungee/log/BungeeLogger.java
+++ b/log/src/main/java/net/md_5/bungee/log/BungeeLogger.java
@@ -4,13 +4,22 @@
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
-import java.util.logging.Level;
+import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import jline.console.ConsoleReader;
public class BungeeLogger extends Logger
{
+ public static BungeeLogger get(String loggerName, String filePattern, ConsoleReader reader) {
+ final LogManager lm = LogManager.getLogManager();
+ Logger logger = lm.getLogger(loggerName);
+ if(!(logger instanceof BungeeLogger)) {
+ logger = new BungeeLogger(loggerName, filePattern, reader);
+ lm.addLogger(logger);
+ }
+ return (BungeeLogger) logger;
+ }
private final Formatter formatter = new ConciseFormatter();
private final LogDispatcher dispatcher = new LogDispatcher( this );
@@ -20,10 +29,11 @@ public class BungeeLogger extends Logger
"CallToPrintStackTrace", "CallToThreadStartDuringObjectConstruction"
})
@SuppressFBWarnings("SC_START_IN_CTOR")
- public BungeeLogger(String loggerName, String filePattern, ConsoleReader reader)
+ private BungeeLogger(String loggerName, String filePattern, ConsoleReader reader)
{
super( loggerName, null );
- setLevel( Level.ALL );
+ setParent(Logger.getLogger(""));
+ setUseParentHandlers(false);
try
{
@@ -32,7 +42,6 @@ public BungeeLogger(String loggerName, String filePattern, ConsoleReader reader)
addHandler( fileHandler );
ColouredWriter consoleHandler = new ColouredWriter( reader );
- consoleHandler.setLevel( Level.INFO );
consoleHandler.setFormatter( formatter );
addHandler( consoleHandler );
} catch ( IOException ex )
diff --git a/module/cmd-alert/pom.xml b/module/cmd-alert/pom.xml
index 223d0f6ff1..4339668bb3 100644
--- a/module/cmd-alert/pom.xml
+++ b/module/cmd-alert/pom.xml
@@ -4,13 +4,12 @@
4.0.0
- net.md-5
+ tc.oc
bungeecord-module
1.11-SNAPSHOT
../pom.xml
- net.md-5
bungeecord-module-cmd-alert
1.11-SNAPSHOT
jar
diff --git a/module/cmd-find/pom.xml b/module/cmd-find/pom.xml
index 7635889da4..7a420ba846 100644
--- a/module/cmd-find/pom.xml
+++ b/module/cmd-find/pom.xml
@@ -4,13 +4,12 @@
4.0.0
- net.md-5
+ tc.oc
bungeecord-module
1.11-SNAPSHOT
../pom.xml
- net.md-5
bungeecord-module-cmd-find
1.11-SNAPSHOT
jar
diff --git a/module/cmd-list/pom.xml b/module/cmd-list/pom.xml
index 76158bcb54..90ebd4f5dd 100644
--- a/module/cmd-list/pom.xml
+++ b/module/cmd-list/pom.xml
@@ -4,13 +4,12 @@
4.0.0
- net.md-5
+ tc.oc
bungeecord-module
1.11-SNAPSHOT
../pom.xml
- net.md-5
bungeecord-module-cmd-list
1.11-SNAPSHOT
jar
diff --git a/module/cmd-send/pom.xml b/module/cmd-send/pom.xml
index 8737541900..37d2df285f 100644
--- a/module/cmd-send/pom.xml
+++ b/module/cmd-send/pom.xml
@@ -4,13 +4,12 @@
4.0.0
- net.md-5
+ tc.oc
bungeecord-module
1.11-SNAPSHOT
../pom.xml
- net.md-5
bungeecord-module-cmd-send
1.11-SNAPSHOT
jar
diff --git a/module/cmd-server/pom.xml b/module/cmd-server/pom.xml
index 8a0ed9de39..f197d3a0ce 100644
--- a/module/cmd-server/pom.xml
+++ b/module/cmd-server/pom.xml
@@ -4,13 +4,12 @@
4.0.0
- net.md-5
+ tc.oc
bungeecord-module
1.11-SNAPSHOT
../pom.xml
- net.md-5
bungeecord-module-cmd-server
1.11-SNAPSHOT
jar
diff --git a/module/pom.xml b/module/pom.xml
index 2d517465b9..a70727e38b 100644
--- a/module/pom.xml
+++ b/module/pom.xml
@@ -4,13 +4,12 @@
4.0.0
- net.md-5
+ tc.oc
bungeecord-parent
1.11-SNAPSHOT
../pom.xml
- net.md-5
bungeecord-module
1.11-SNAPSHOT
pom
@@ -33,7 +32,7 @@
- net.md-5
+ tc.oc
bungeecord-api
${project.version}
compile
diff --git a/module/reconnect-yaml/pom.xml b/module/reconnect-yaml/pom.xml
index c08ad70d47..11cf9d51ff 100644
--- a/module/reconnect-yaml/pom.xml
+++ b/module/reconnect-yaml/pom.xml
@@ -4,13 +4,12 @@
4.0.0
- net.md-5
+ tc.oc
bungeecord-module
1.11-SNAPSHOT
../pom.xml
- net.md-5
bungeecord-module-reconnect-yaml
1.11-SNAPSHOT
jar
diff --git a/native/pom.xml b/native/pom.xml
index 940d257e1e..7a2dadce14 100644
--- a/native/pom.xml
+++ b/native/pom.xml
@@ -4,13 +4,12 @@
4.0.0
- net.md-5
+ tc.oc
bungeecord-parent
1.11-SNAPSHOT
../pom.xml
- net.md-5
bungeecord-native
1.11-SNAPSHOT
jar
diff --git a/pom.xml b/pom.xml
index 7f9564c6d7..4a17e53301 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
9
- net.md-5
+ tc.oc
bungeecord-parent
1.11-SNAPSHOT
pom
@@ -29,6 +29,16 @@
repo
+
+
+ overcast-deployment
+ https://repo.oc.tc/content/repositories/releases
+
+
+ overcast-deployment
+ https://repo.oc.tc/content/repositories/snapshots
+
+
@@ -117,31 +127,6 @@
-
- org.codehaus.mojo
- animal-sniffer-maven-plugin
- 1.13
-
-
- process-classes
-
- check
-
-
-
-
-
- java.lang.ClassLoader
- java.lang.Throwable
- java.util.Locale
-
-
- org.codehaus.mojo.signature
- java16
- 1.1
-
-
-
org.apache.maven.plugins
diff --git a/protocol/pom.xml b/protocol/pom.xml
index 63d04980a9..d73892d9a4 100644
--- a/protocol/pom.xml
+++ b/protocol/pom.xml
@@ -4,13 +4,12 @@
4.0.0
- net.md-5
+ tc.oc
bungeecord-parent
1.11-SNAPSHOT
../pom.xml
- net.md-5
bungeecord-protocol
1.11-SNAPSHOT
jar
@@ -20,7 +19,7 @@
- net.md-5
+ tc.oc
bungeecord-chat
${project.version}
compile
diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/BadPacketException.java b/protocol/src/main/java/net/md_5/bungee/protocol/BadPacketException.java
index 6c0ef4dfa0..d3ee377d8b 100644
--- a/protocol/src/main/java/net/md_5/bungee/protocol/BadPacketException.java
+++ b/protocol/src/main/java/net/md_5/bungee/protocol/BadPacketException.java
@@ -1,6 +1,8 @@
package net.md_5.bungee.protocol;
-public class BadPacketException extends RuntimeException
+import io.netty.handler.codec.DecoderException;
+
+public class BadPacketException extends DecoderException
{
public BadPacketException(String message)
diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java b/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java
index e7cb380342..f7566f625c 100644
--- a/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java
+++ b/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java
@@ -30,8 +30,14 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List
- net.md-5
+ tc.oc
bungeecord-api
${project.version}
compile
- net.md-5
+ tc.oc
bungeecord-log
${project.version}
compile
- net.md-5
+ tc.oc
bungeecord-native
${project.version}
compile
- net.md-5
+ tc.oc
bungeecord-protocol
${project.version}
compile
- net.md-5
+ tc.oc
bungeecord-query
${project.version}
compile
diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java
index 159dab671a..b1a71344c0 100644
--- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java
+++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java
@@ -4,6 +4,7 @@
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
@@ -35,6 +36,7 @@
import java.util.MissingResourceException;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
+import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
@@ -55,6 +57,8 @@
import net.md_5.bungee.api.ServerPing;
import net.md_5.bungee.api.Title;
import net.md_5.bungee.api.chat.BaseComponent;
+import net.md_5.bungee.api.chat.ScoreComponent;
+import net.md_5.bungee.api.chat.SelectorComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.chat.TranslatableComponent;
import net.md_5.bungee.api.config.ConfigurationAdapter;
@@ -64,6 +68,8 @@
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.api.plugin.PluginManager;
import net.md_5.bungee.chat.ComponentSerializer;
+import net.md_5.bungee.chat.ScoreComponentSerializer;
+import net.md_5.bungee.chat.SelectorComponentSerializer;
import net.md_5.bungee.chat.TextComponentSerializer;
import net.md_5.bungee.chat.TranslatableComponentSerializer;
import net.md_5.bungee.command.CommandBungee;
@@ -88,6 +94,7 @@
import net.md_5.bungee.scheduler.BungeeScheduler;
import net.md_5.bungee.util.CaseInsensitiveMap;
import org.fusesource.jansi.AnsiConsole;
+import tc.oc.minecraft.api.plugin.PluginFinder;
/**
* Main BungeeCord proxy class.
@@ -130,7 +137,7 @@ public class BungeeCord extends ProxyServer
* Plugin manager.
*/
@Getter
- public final PluginManager pluginManager = new PluginManager( this );
+ public final PluginManager pluginManager;
@Getter
@Setter
private ReconnectHandler reconnectHandler;
@@ -150,24 +157,14 @@ public class BungeeCord extends ProxyServer
.registerTypeAdapter( BaseComponent.class, new ComponentSerializer() )
.registerTypeAdapter( TextComponent.class, new TextComponentSerializer() )
.registerTypeAdapter( TranslatableComponent.class, new TranslatableComponentSerializer() )
+ .registerTypeAdapter( SelectorComponent.class, new SelectorComponentSerializer() )
+ .registerTypeAdapter( ScoreComponent.class, new ScoreComponentSerializer() )
.registerTypeAdapter( ServerPing.PlayerInfo.class, new PlayerInfoSerializer() )
.registerTypeAdapter( Favicon.class, Favicon.getFaviconTypeAdapter() ).create();
@Getter
private ConnectionThrottle connectionThrottle;
private final ModuleManager moduleManager = new ModuleManager();
-
- {
- // TODO: Proper fallback when we interface the manager
- getPluginManager().registerCommand( null, new CommandReload() );
- getPluginManager().registerCommand( null, new CommandEnd() );
- getPluginManager().registerCommand( null, new CommandIP() );
- getPluginManager().registerCommand( null, new CommandBungee() );
- getPluginManager().registerCommand( null, new CommandPerms() );
-
- registerChannel( "BungeeCord" );
- }
-
public static BungeeCord getInstance()
{
return (BungeeCord) ProxyServer.getInstance();
@@ -179,7 +176,8 @@ public BungeeCord() throws IOException
// Java uses ! to indicate a resource inside of a jar/zip/other container. Running Bungee from within a directory that has a ! will cause this to muck up.
Preconditions.checkState( new File( "." ).getAbsolutePath().indexOf( '!' ) == -1, "Cannot use BungeeCord in directory with ! in path." );
- System.setSecurityManager( new BungeeSecurityManager() );
+ // Overcast - disable security manager
+ // System.setSecurityManager( new BungeeSecurityManager() );
try
{
@@ -211,8 +209,10 @@ public BungeeCord() throws IOException
consoleReader = new ConsoleReader();
consoleReader.setExpandEvents( false );
- logger = new BungeeLogger( "BungeeCord", "proxy.log", consoleReader );
- System.setErr( new PrintStream( new LoggingOutputStream( logger, Level.SEVERE ), true ) );
+ logger = BungeeLogger.get( "BungeeCord", "proxy.log", consoleReader );
+
+ // Overcast - stderr gets a lot of non-error output, so log it at WARNING level instead of SEVERE
+ System.setErr( new PrintStream( new LoggingOutputStream( logger, Level.WARNING ), true ) );
System.setOut( new PrintStream( new LoggingOutputStream( logger, Level.INFO ), true ) );
if ( !Boolean.getBoolean( "net.md_5.bungee.native.disable" ) )
@@ -232,6 +232,17 @@ public BungeeCord() throws IOException
logger.info( "Using standard Java compressor. To enable zero copy compression, run on 64 bit Linux" );
}
}
+
+ pluginManager = new PluginManager( this );
+
+ // TODO: Proper fallback when we interface the manager
+ getPluginManager().registerCommand( null, new CommandReload() );
+ getPluginManager().registerCommand( null, new CommandEnd() );
+ getPluginManager().registerCommand( null, new CommandIP() );
+ getPluginManager().registerCommand( null, new CommandBungee() );
+ getPluginManager().registerCommand( null, new CommandPerms() );
+
+ registerChannel( "BungeeCord" );
}
/**
@@ -411,7 +422,7 @@ public void run()
// TODO: Fix this shit
getLogger().info( "Disabling plugins" );
- for ( Plugin plugin : Lists.reverse( new ArrayList<>( pluginManager.getPlugins() ) ) )
+ for ( Plugin plugin : Lists.reverse( new ArrayList<>( pluginManager.getEnabledPlugins() ) ) )
{
try
{
@@ -556,10 +567,16 @@ public Map getServers()
return config.getServers();
}
+ @Override
+ public Map getServersCopy()
+ {
+ return config.getServersCopy();
+ }
+
@Override
public ServerInfo getServerInfo(String name)
{
- return getServers().get( name );
+ return config.getServerInfo( name );
}
@Override
@@ -606,6 +623,11 @@ public ServerInfo constructServerInfo(String name, InetSocketAddress address, St
return new BungeeServerInfo( name, address, motd, restricted );
}
+ @Override
+ public tc.oc.minecraft.api.command.ConsoleCommandSender getConsoleSender() {
+ return ConsoleCommandSender.getInstance();
+ }
+
@Override
public CommandSender getConsole()
{
@@ -695,4 +717,14 @@ public Title createTitle()
{
return new BungeeTitle();
}
+
+ @Override
+ public Set getProtocolVersions() {
+ return ImmutableSet.copyOf(ProtocolConstants.SUPPORTED_VERSION_IDS);
+ }
+
+ @Override
+ public PluginFinder getPluginFinder() {
+ return pluginManager;
+ }
}
diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java b/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java
index 1dd0aeba8a..f8699d26ad 100644
--- a/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java
+++ b/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java
@@ -13,6 +13,8 @@
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
+import java.util.UUID;
+
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Synchronized;
@@ -69,6 +71,32 @@ public Collection getPlayers()
return Collections.unmodifiableCollection( new HashSet( players ) );
}
+ @Override
+ public Collection extends ProxiedPlayer> getOnlinePlayers()
+ {
+ return getPlayers();
+ }
+
+ @Override
+ public ProxiedPlayer getPlayerExact(String name)
+ {
+ for(ProxiedPlayer player : getPlayers())
+ {
+ if(name.equalsIgnoreCase(player.getName())) return player;
+ }
+ return null;
+ }
+
+ @Override
+ public ProxiedPlayer getPlayer(UUID id)
+ {
+ for(ProxiedPlayer player : getPlayers())
+ {
+ if(id.equals(player.getUniqueId())) return player;
+ }
+ return null;
+ }
+
@Override
public boolean canAccess(CommandSender player)
{
diff --git a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java
index 2014b0c492..90d91d7d2d 100644
--- a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java
+++ b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java
@@ -33,6 +33,7 @@
import net.md_5.bungee.protocol.packet.Handshake;
import net.md_5.bungee.protocol.packet.Kick;
import net.md_5.bungee.protocol.packet.Login;
+import net.md_5.bungee.protocol.packet.LoginRequest;
import net.md_5.bungee.protocol.packet.LoginSuccess;
import net.md_5.bungee.protocol.packet.PluginMessage;
import net.md_5.bungee.protocol.packet.Respawn;
@@ -48,6 +49,7 @@ public class ServerConnector extends PacketHandler
private final UserConnection user;
private final BungeeServerInfo target;
private State thisState = State.LOGIN_SUCCESS;
+ private final String fakeUsername;
@Getter
private ForgeServerHandler handshakeHandler;
private boolean obsolete;
@@ -105,7 +107,7 @@ public void connected(ChannelWrapper channel) throws Exception
channel.write( copiedHandshake );
channel.setProtocol( Protocol.LOGIN );
- channel.write( user.getPendingConnection().getLoginRequest() );
+ channel.write(new LoginRequest(fakeUsername != null ? fakeUsername : user.getPendingConnection().getName()));
}
@Override
diff --git a/proxy/src/main/java/net/md_5/bungee/UserConnection.java b/proxy/src/main/java/net/md_5/bungee/UserConnection.java
index d620fbf598..652224bc71 100644
--- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java
+++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java
@@ -149,6 +149,9 @@ public void sendPacket(DefinedPacket packet)
}
};
+ @Getter
+ private Throwable disconnectException;
+
public void init()
{
this.entityRewrite = EntityMap.getEntityMap( getPendingConnection().getVersion() );
@@ -241,7 +244,11 @@ public ServerInfo updateAndGetNextServer(ServerInfo currentTarget)
return next;
}
- public void connect(ServerInfo info, final Callback callback, final boolean retry)
+ public void connect(ServerInfo info, final Callback callback, final boolean retry) {
+ connect(info, callback, retry, false);
+ }
+
+ public void connect(ServerInfo info, final Callback callback, final boolean retry, final boolean quiet)
{
Preconditions.checkNotNull( info, "info" );
@@ -256,6 +263,7 @@ public void connect(ServerInfo info, final Callback callback, final boo
}
final BungeeServerInfo target = (BungeeServerInfo) event.getTarget(); // Update in case the event changed target
+ final String fakeUsername = event.getFakeUsername();
if ( getServer() != null && Objects.equal( getServer().getInfo(), target ) )
{
@@ -264,7 +272,9 @@ public void connect(ServerInfo info, final Callback callback, final boo
callback.done( false, null );
}
- sendMessage( bungee.getTranslation( "already_connected" ) );
+ if(!quiet) {
+ sendMessage( bungee.getTranslation( "already_connected" ) );
+ }
return;
}
if ( pendingConnects.contains( target ) )
@@ -274,7 +284,9 @@ public void connect(ServerInfo info, final Callback callback, final boo
callback.done( false, null );
}
- sendMessage( bungee.getTranslation( "already_connecting" ) );
+ if(!quiet) {
+ sendMessage( bungee.getTranslation( "already_connecting" ) );
+ }
return;
}
@@ -288,7 +300,7 @@ protected void initChannel(Channel ch) throws Exception
PipelineUtils.BASE.initChannel( ch );
ch.pipeline().addAfter( PipelineUtils.FRAME_DECODER, PipelineUtils.PACKET_DECODER, new MinecraftDecoder( Protocol.HANDSHAKE, false, getPendingConnection().getVersion() ) );
ch.pipeline().addAfter( PipelineUtils.FRAME_PREPENDER, PipelineUtils.PACKET_ENCODER, new MinecraftEncoder( Protocol.HANDSHAKE, false, getPendingConnection().getVersion() ) );
- ch.pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( bungee, UserConnection.this, target ) );
+ ch.pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( bungee, UserConnection.this, target, fakeUsername ) );
}
};
ChannelFutureListener listener = new ChannelFutureListener()
@@ -310,13 +322,14 @@ public void operationComplete(ChannelFuture future) throws Exception
ServerInfo def = updateAndGetNextServer( target );
if ( retry && def != null && ( getServer() == null || def != getServer().getInfo() ) )
{
- sendMessage( bungee.getTranslation( "fallback_lobby" ) );
+ if(!quiet) {
+ sendMessage( bungee.getTranslation( "fallback_lobby" ) );
+ }
connect( def, null, false );
} else if ( dimensionChange )
{
disconnect( bungee.getTranslation( "fallback_kick", future.cause().getClass().getName() ) );
- } else
- {
+ } else if(!quiet) {
sendMessage( bungee.getTranslation( "fallback_kick", future.cause().getClass().getName() ) );
}
}
@@ -336,6 +349,13 @@ public void operationComplete(ChannelFuture future) throws Exception
b.connect().addListener( listener );
}
+ public void disconnect(Throwable exception) {
+ if(disconnectException == null) {
+ disconnectException = exception;
+ }
+ disconnect(Util.exception(exception));
+ }
+
@Override
public void disconnect(String reason)
{
@@ -550,6 +570,12 @@ public Locale getLocale()
return ( locale == null && settings != null ) ? locale = Locale.forLanguageTag( settings.getLocale().replaceAll( "_", "-" ) ) : locale;
}
+ @Override
+ public Locale getCurrentLocale()
+ {
+ return getLocale();
+ }
+
@Override
public boolean isForgeUser()
{
@@ -622,4 +648,9 @@ public boolean isConnected()
{
return !ch.isClosed();
}
+
+ @Override
+ public int getProtocolVersion() {
+ return getPendingConnection().getVersion();
+ }
}
diff --git a/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java b/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java
index 49dde16ffe..326e89abf9 100644
--- a/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java
+++ b/proxy/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java
@@ -10,7 +10,7 @@
/**
* Command sender representing the proxy console.
*/
-public class ConsoleCommandSender implements CommandSender
+public class ConsoleCommandSender implements CommandSender, tc.oc.minecraft.api.command.ConsoleCommandSender
{
@Getter
diff --git a/proxy/src/main/java/net/md_5/bungee/conf/Configuration.java b/proxy/src/main/java/net/md_5/bungee/conf/Configuration.java
index 25d87d97d6..cacb30af22 100644
--- a/proxy/src/main/java/net/md_5/bungee/conf/Configuration.java
+++ b/proxy/src/main/java/net/md_5/bungee/conf/Configuration.java
@@ -1,6 +1,7 @@
package net.md_5.bungee.conf;
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
import gnu.trove.map.TMap;
import java.io.File;
import java.io.IOException;
@@ -11,6 +12,7 @@
import java.util.logging.Level;
import javax.imageio.ImageIO;
import lombok.Getter;
+import lombok.Synchronized;
import net.md_5.bungee.api.Favicon;
import net.md_5.bungee.api.ProxyConfig;
import net.md_5.bungee.api.ProxyServer;
@@ -57,6 +59,7 @@ public class Configuration implements ProxyConfig
private boolean ipForward;
private Favicon favicon;
private int compressionThreshold = 256;
+ private boolean requireAllPlugins;
public void load()
{
@@ -84,6 +87,7 @@ public void load()
throttle = adapter.getInt( "connection_throttle", throttle );
ipForward = adapter.getBoolean( "ip_forward", ipForward );
compressionThreshold = adapter.getInt( "network_compression_threshold", compressionThreshold );
+ requireAllPlugins = adapter.getBoolean( "require_all_plugins", requireAllPlugins );
disabledCommands = new CaseInsensitiveSet( (Collection) adapter.getList( "disabled_commands", Arrays.asList( "disabledcommandhere" ) ) );
@@ -142,4 +146,69 @@ public Favicon getFaviconObject()
{
return favicon;
}
+
+ @Override
+ @Synchronized("servers")
+ public Map getServersCopy() {
+ return ImmutableMap.copyOf( servers );
+ }
+
+ @Override
+ @Synchronized("servers")
+ public ServerInfo getServerInfo(String name)
+ {
+ return this.servers.get( name );
+ }
+
+ @Override
+ @Synchronized("servers")
+ public ServerInfo addServer(ServerInfo server)
+ {
+ return this.servers.put( server.getName(), server );
+ }
+
+ @Override
+ @Synchronized("servers")
+ public boolean addServers(Collection servers)
+ {
+ boolean changed = false;
+ for ( ServerInfo server : servers )
+ {
+ if ( server != this.servers.put( server.getName(), server ) ) changed = true;
+ }
+ return changed;
+ }
+
+ @Override
+ @Synchronized("servers")
+ public ServerInfo removeServerNamed(String name)
+ {
+ return this.servers.remove( name );
+ }
+
+ @Override
+ @Synchronized("servers")
+ public ServerInfo removeServer(ServerInfo server)
+ {
+ return this.servers.remove( server.getName() );
+ }
+
+ @Override
+ @Synchronized("servers")
+ public boolean removeServersNamed(Collection names)
+ {
+ return this.servers.keySet().removeAll( names );
+ }
+
+ @Override
+ @Synchronized("servers")
+ public boolean removeServers(Collection servers)
+ {
+ boolean changed = false;
+ for ( ServerInfo server : servers )
+ {
+ if ( null != this.servers.remove( server.getName() ) ) changed = true;
+ }
+ return changed;
+ }
}
diff --git a/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java
index c2dd942ff3..28f50bc6a5 100644
--- a/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java
+++ b/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java
@@ -68,7 +68,7 @@ public void exception(Throwable t) throws Exception
con.sendMessage( bungee.getTranslation( "server_went_down" ) );
} else
{
- con.disconnect( Util.exception( t ) );
+ con.disconnect( t );
}
}
@@ -328,6 +328,14 @@ public void handle(PluginMessage pluginMessage) throws Exception
con.connect( server );
}
}
+ if ( subChannel.equals( "ConnectQuiet" ) )
+ {
+ ServerInfo server = bungee.getServerInfo( in.readUTF() );
+ if ( server != null )
+ {
+ con.connect( server, null, false, true );
+ }
+ }
if ( subChannel.equals( "ConnectOther" ) )
{
ProxiedPlayer player = bungee.getPlayer( in.readUTF() );
diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java
index bdcbdb7ad1..adab8fa9ba 100644
--- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java
+++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java
@@ -111,7 +111,7 @@ public boolean shouldHandle(PacketWrapper packet) throws Exception
private enum State
{
- HANDSHAKE, STATUS, PING, USERNAME, ENCRYPT, FINISHED;
+ HANDSHAKE, STATUS, USERNAME, ENCRYPT, FINISHED;
}
@Override
@@ -243,14 +243,12 @@ public void done(ProxyPingEvent pingResult, Throwable error)
motd, BungeeCord.getInstance().config.getFaviconObject() ),
null );
}
-
- thisState = State.PING;
}
@Override
public void handle(PingPacket ping) throws Exception
{
- Preconditions.checkState( thisState == State.PING, "Not expecting PING" );
+ Preconditions.checkState( thisState == State.STATUS, "Not expecting PING" );
unsafe.sendPacket( ping );
disconnect( "" );
}
@@ -544,7 +542,7 @@ public void disconnect(final BaseComponent... reason)
@Override
public void run()
{
- if ( thisState != State.STATUS && thisState != State.PING )
+ if ( thisState != State.STATUS )
{
unsafe().sendPacket( new Kick( ComponentSerializer.toString( reason ) ) );
}
diff --git a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java
index 92d1da05b8..bf74eb1d11 100644
--- a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java
+++ b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java
@@ -44,7 +44,7 @@ public UpstreamBridge(ProxyServer bungee, UserConnection con)
@Override
public void exception(Throwable t) throws Exception
{
- con.disconnect( Util.exception( t ) );
+ con.disconnect( t );
}
@Override
diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java
index 34ddc19f5e..6ce1078385 100644
--- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java
+++ b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java
@@ -5,6 +5,7 @@
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import net.md_5.bungee.protocol.DefinedPacket;
+import net.md_5.bungee.protocol.Protocol;
import net.md_5.bungee.protocol.ProtocolConstants;
/**
@@ -107,12 +108,15 @@ private static void rewrite(ByteBuf packet, int oldId, int newId, boolean[] ints
int packetId = DefinedPacket.readVarInt( packet );
int packetIdLength = packet.readerIndex() - readerIndex;
- if ( ints[packetId] )
+ if ( 0 <= packetId && packetId < Protocol.MAX_PACKET_ID)
{
- rewriteInt( packet, oldId, newId, readerIndex + packetIdLength );
- } else if ( varints[packetId] )
- {
- rewriteVarInt( packet, oldId, newId, readerIndex + packetIdLength );
+ if ( ints[packetId] )
+ {
+ rewriteInt( packet, oldId, newId, readerIndex + packetIdLength );
+ } else if ( varints[packetId] )
+ {
+ rewriteVarInt(packet, oldId, newId, readerIndex + packetIdLength);
+ }
}
packet.readerIndex( readerIndex );
}
diff --git a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java
index 0b0dd736e5..a2bc0831dc 100644
--- a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java
+++ b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java
@@ -13,6 +13,8 @@
import net.md_5.bungee.protocol.PacketWrapper;
import net.md_5.bungee.protocol.Protocol;
+import java.util.NoSuchElementException;
+
public class ChannelWrapper
{
@@ -110,7 +112,12 @@ public void setCompressionThreshold(int compressionThreshold)
{
if ( ch.pipeline().get( PacketCompressor.class ) == null && compressionThreshold != -1 )
{
- addBefore( PipelineUtils.PACKET_ENCODER, "compress", new PacketCompressor() );
+ try {
+ addBefore(PipelineUtils.PACKET_ENCODER, "compress", new PacketCompressor());
+ } catch(NoSuchElementException ex) {
+ // Sometimes packet-encoder is not in the pipeline, probably when the client disconnects soon after connecting
+ return;
+ }
}
if ( compressionThreshold != -1 )
{
diff --git a/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java b/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java
index 3efeff8178..bf504a110c 100644
--- a/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java
+++ b/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java
@@ -7,6 +7,8 @@
import io.netty.handler.timeout.ReadTimeoutException;
import java.io.IOException;
import java.util.logging.Level;
+import java.util.logging.LogRecord;
+
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.connection.CancelSendSignal;
import net.md_5.bungee.connection.InitialHandler;
@@ -119,7 +121,10 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws E
} );
} else
{
- ProxyServer.getInstance().getLogger().log( Level.SEVERE, handler + " - encountered exception", cause );
+ final LogRecord record = new LogRecord( Level.SEVERE, handler + " - encountered exception" );
+ record.setThrown( cause );
+ record.setParameters( new Object[]{ ctx.channel().remoteAddress() } );
+ ProxyServer.getInstance().getLogger().log( record );
}
if ( handler != null )
diff --git a/proxy/src/test/java/net/md_5/bungee/chat/ComponentsTest.java b/proxy/src/test/java/net/md_5/bungee/chat/ComponentsTest.java
index 004a2b7a88..e9985eac40 100644
--- a/proxy/src/test/java/net/md_5/bungee/chat/ComponentsTest.java
+++ b/proxy/src/test/java/net/md_5/bungee/chat/ComponentsTest.java
@@ -1,5 +1,7 @@
package net.md_5.bungee.chat;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
@@ -35,8 +37,8 @@ public void testLegacyConverter()
Assert.assertEquals( "Text http://spigotmc.org google.com/test", BaseComponent.toPlainText( test2 ) );
//The extra ChatColor.WHITEs are sometimes inserted when not needed but it doesn't change the result
- Assert.assertEquals( ChatColor.WHITE + "Text " + ChatColor.WHITE + "http://spigotmc.org" + ChatColor.WHITE
- + " " + ChatColor.GREEN + "google.com/test", BaseComponent.toLegacyText( test2 ) );
+ Assert.assertEquals( ChatColor.WHITE + "Text " + ChatColor.WHITE + "http://spigotmc.org" + ChatColor.WHITE + " " + ChatColor.GREEN + "google.com/test",
+ BaseComponent.toLegacyText( ChatColor.WHITE, ImmutableSet.of(), test2 ) );
ClickEvent url1 = test2[1].getClickEvent();
Assert.assertNotNull( url1 );
@@ -60,13 +62,13 @@ public void testTranslateComponent()
Assert.assertEquals( "Given Golden Sword * 5 to thinkofdeath", translatableComponent.toPlainText() );
Assert.assertEquals( ChatColor.WHITE + "Given " + ChatColor.AQUA + "Golden Sword" + ChatColor.WHITE
- + " * " + ChatColor.WHITE + "5" + ChatColor.WHITE + " to " + ChatColor.WHITE + "thinkofdeath",
- translatableComponent.toLegacyText() );
+ + " * 5 to thinkofdeath",
+ translatableComponent.toLegacyText(ChatColor.WHITE) );
TranslatableComponent positional = new TranslatableComponent( "book.pageIndicator", "5", "50" );
Assert.assertEquals( "Page 5 of 50", positional.toPlainText() );
- Assert.assertEquals( ChatColor.WHITE + "Page " + ChatColor.WHITE + "5" + ChatColor.WHITE + " of " + ChatColor.WHITE + "50", positional.toLegacyText() );
+ Assert.assertEquals( ChatColor.WHITE + "Page 5 of 50", positional.toLegacyText(ChatColor.WHITE) );
}
@Test
@@ -123,50 +125,58 @@ public void testBuilderFormatRetention()
Assert.assertEquals( eventRetention[1].getClickEvent(), testClickEvent );
}
- @Test(expected = IllegalArgumentException.class)
+ @Test
public void testLoopSimple()
{
TextComponent component = new TextComponent( "Testing" );
- component.addExtra( component );
- ComponentSerializer.toString( component );
+
+ try {
+ component.addExtra( component );
+ Assert.fail();
+ } catch(IllegalArgumentException ignored) {}
+
+ try {
+ component.setExtra( component );
+ Assert.fail();
+ } catch(IllegalArgumentException ignored) {}
+
+ try {
+ component.setExtra(ImmutableList.of(component));
+ Assert.fail();
+ } catch(IllegalArgumentException ignored) {}
}
- @Test(expected = IllegalArgumentException.class)
+ @Test
public void testLoopComplex()
{
TextComponent a = new TextComponent( "A" );
TextComponent b = new TextComponent( "B" );
- b.setColor( ChatColor.AQUA );
TextComponent c = new TextComponent( "C" );
- c.setColor( ChatColor.RED );
a.addExtra( b );
b.addExtra( c );
- c.addExtra( a );
- ComponentSerializer.toString( a );
+
+ try {
+ c.addExtra( a );
+ Assert.fail();
+ } catch(IllegalArgumentException ignored) {}
}
@Test
- public void testRepeated()
- {
- TextComponent a = new TextComponent( "A" );
- TextComponent b = new TextComponent( "B" );
- b.setColor( ChatColor.AQUA );
- a.addExtra( b );
- a.addExtra( b );
- ComponentSerializer.toString( a );
+ public void testLoopInTranslatable() {
+ TranslatableComponent c = new TranslatableComponent("hi");
+ try {
+ c.addWith(c);
+ Assert.fail();
+ } catch(IllegalArgumentException ignored) {}
}
- @Test(expected = IllegalArgumentException.class)
- public void testRepeatedError()
+ @Test
+ public void testRepeated()
{
TextComponent a = new TextComponent( "A" );
TextComponent b = new TextComponent( "B" );
b.setColor( ChatColor.AQUA );
- TextComponent c = new TextComponent( "C" );
- c.setColor( ChatColor.RED );
a.addExtra( b );
- a.addExtra( c );
- c.addExtra( a );
a.addExtra( b );
ComponentSerializer.toString( a );
}
diff --git a/query/pom.xml b/query/pom.xml
index fbadb18910..65f7684dd2 100644
--- a/query/pom.xml
+++ b/query/pom.xml
@@ -4,13 +4,12 @@
4.0.0
- net.md-5
+ tc.oc
bungeecord-parent
1.11-SNAPSHOT
../pom.xml
- net.md-5
bungeecord-query
1.11-SNAPSHOT
jar
@@ -26,7 +25,7 @@
compile
- net.md-5
+ tc.oc
bungeecord-api
${project.version}
compile