diff --git a/eaglerbungee/.gitignore b/eaglerbungee/.gitignore
new file mode 100644
index 0000000..0fde6ec
--- /dev/null
+++ b/eaglerbungee/.gitignore
@@ -0,0 +1,37 @@
+# Eclipse stuff
+.classpath
+.project
+.settings/
+
+# netbeans
+nbproject/
+nbactions.xml
+
+# we use maven!
+build.xml
+
+# maven
+target/
+dependency-reduced-pom.xml
+
+# vim
+.*.sw[a-p]
+
+# various other potential build files
+build/
+bin/
+dist/
+manifest.mf
+
+# Mac filesystem dust
+.DS_Store/
+
+# intellij
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# other files
+*.log*
+*.yml
diff --git a/eaglerbungee/Java-WebSocket-1.5.1-with-dependencies.jar b/eaglerbungee/Java-WebSocket-1.5.1-with-dependencies.jar
new file mode 100644
index 0000000..8f103a7
Binary files /dev/null and b/eaglerbungee/Java-WebSocket-1.5.1-with-dependencies.jar differ
diff --git a/eaglerbungee/build.gradle b/eaglerbungee/build.gradle
new file mode 100644
index 0000000..e724149
--- /dev/null
+++ b/eaglerbungee/build.gradle
@@ -0,0 +1,39 @@
+plugins {
+ id "java"
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs(
+ "src/main/java"
+ )
+ }
+ }
+}
+
+repositories {
+ mavenCentral()
+}
+
+tasks.withType(JavaCompile) {
+ options.warnings = false
+ options.compilerArgs << "-Xmaxerrs" << "1000"
+}
+
+dependencies {
+ implementation fileTree(dir: '.', include: '*.jar')
+}
+
+jar {
+ duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+
+ manifest {
+ attributes(
+ 'Main-Class': 'net.md_5.bungee.BungeeCord'
+ )
+ }
+ from {
+ configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/bungee-deps.jar b/eaglerbungee/bungee-deps.jar
new file mode 100644
index 0000000..36b6759
Binary files /dev/null and b/eaglerbungee/bungee-deps.jar differ
diff --git a/eaglerbungee/gradle.properties b/eaglerbungee/gradle.properties
new file mode 100644
index 0000000..2913ef0
--- /dev/null
+++ b/eaglerbungee/gradle.properties
@@ -0,0 +1,2 @@
+org.gradle.jvmargs=-Xmx2G -Xms2G
+
diff --git a/eaglerbungee/gradle/wrapper/gradle-wrapper.jar b/eaglerbungee/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..249e583
Binary files /dev/null and b/eaglerbungee/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/eaglerbungee/gradle/wrapper/gradle-wrapper.properties b/eaglerbungee/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..e9da0e8
--- /dev/null
+++ b/eaglerbungee/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sun Feb 11 10:50:57 EST 2024
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/eaglerbungee/gradlew b/eaglerbungee/gradlew
new file mode 100755
index 0000000..1b6c787
--- /dev/null
+++ b/eaglerbungee/gradlew
@@ -0,0 +1,234 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${0##*/}
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/eaglerbungee/gradlew.bat b/eaglerbungee/gradlew.bat
new file mode 100644
index 0000000..ac1b06f
--- /dev/null
+++ b/eaglerbungee/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/eaglerbungee/nb-configuration.xml b/eaglerbungee/nb-configuration.xml
new file mode 100644
index 0000000..7e46592
--- /dev/null
+++ b/eaglerbungee/nb-configuration.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+ project
+ NEW_LINE
+ NEW_LINE
+ NEW_LINE
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+
+
diff --git a/eaglerbungee/pom.xml b/eaglerbungee/pom.xml
new file mode 100644
index 0000000..768bda2
--- /dev/null
+++ b/eaglerbungee/pom.xml
@@ -0,0 +1,114 @@
+
+
+ 4.0.0
+
+
+ org.sonatype.oss
+ oss-parent
+ 7
+
+
+ net.md-5
+ bungeecord-parent
+ 1.6.4-SNAPSHOT
+ pom
+
+ BungeeCord
+ Parent project for all BungeeCord modules.
+ https://github.com/SpigotMC/BungeeCord
+ 2012
+
+ Elastic Portal Suite
+ https://github.com/SpigotMC
+
+
+
+ The BSD 3-Clause License
+ http://opensource.org/licenses/BSD-3-Clause
+ repo
+
+
+
+
+
+ md_5
+
+
+
+
+ api
+ bootstrap
+ config
+ event
+ protocol
+ proxy
+ query
+
+
+
+ scm:git:git@github.com:SpigotMC/BungeeCord.git
+ scm:git:git@github.com:SpigotMC/BungeeCord.git
+ git@github.com:SpigotMC/BungeeCord.git
+
+
+ GitHub
+ https://github.com/SpigotMC/BungeeCord/issues
+
+
+ jenkins
+ http://ci.md-5.net/job/BungeeCord
+
+
+
+ unknown
+ 4.0.9.Final
+ UTF-8
+
+
+
+
+ junit
+ junit
+ 4.11
+ test
+
+
+ org.projectlombok
+ lombok
+ 1.12.2
+ provided
+
+
+
+
+
+
+ com.lukegb.mojo
+ gitdescribe-maven-plugin
+ 1.3
+
+ git-${project.name}-${project.version}-
+ -${build.number}
+
+
+
+ compile
+
+ gitdescribe
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 2.5.1
+
+ 1.7
+ 1.7
+
+
+
+
+
diff --git a/eaglerbungee/settings.gradle b/eaglerbungee/settings.gradle
new file mode 100644
index 0000000..66b966e
--- /dev/null
+++ b/eaglerbungee/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'bungee-dist'
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/Bootstrap.java b/eaglerbungee/src/main/java/net/md_5/bungee/Bootstrap.java
new file mode 100644
index 0000000..aa43426
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/Bootstrap.java
@@ -0,0 +1,44 @@
+package net.md_5.bungee;
+
+import java.util.Arrays;
+import java.util.List;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.command.ConsoleCommandSender;
+
+public class Bootstrap
+{
+
+ private static List list(String... params)
+ {
+ return Arrays.asList( params );
+ }
+
+ /**
+ * Starts a new instance of BungeeCord.
+ *
+ * @param args command line arguments, currently none are used
+ * @throws Exception when the server cannot be started
+ */
+ public static void main(String[] args) throws Exception
+ {
+ System.setProperty( "java.net.preferIPv4Stack", "true" );
+
+ BungeeCord bungee = new BungeeCord();
+ ProxyServer.setInstance( bungee );
+ bungee.getLogger().info( "Enabled BungeeCord version " + bungee.getVersion() );
+ bungee.start();
+
+ while ( bungee.isRunning )
+ {
+ String line = bungee.getConsoleReader().readLine( ">" );
+ if ( line != null )
+ {
+ if ( !bungee.getPluginManager().dispatchCommand( ConsoleCommandSender.getInstance(), line ) )
+ {
+ bungee.getConsole().sendMessage( ChatColor.RED + "Command not found" );
+ }
+ }
+ }
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/BungeeCord.java b/eaglerbungee/src/main/java/net/md_5/bungee/BungeeCord.java
new file mode 100644
index 0000000..e9f2763
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/BungeeCord.java
@@ -0,0 +1,574 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.SocketAddress;
+import java.util.*;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.fusesource.jansi.AnsiConsole;
+
+import com.google.common.io.ByteStreams;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelException;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.MultithreadEventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.util.AttributeKey;
+import io.netty.util.concurrent.GenericFutureListener;
+import jline.UnsupportedTerminal;
+import jline.console.ConsoleReader;
+import jline.internal.Log;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.ReconnectHandler;
+import net.md_5.bungee.api.config.ConfigurationAdapter;
+import net.md_5.bungee.api.config.ListenerInfo;
+import net.md_5.bungee.api.config.ServerInfo;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.plugin.Command;
+import net.md_5.bungee.api.plugin.Plugin;
+import net.md_5.bungee.api.plugin.PluginManager;
+import net.md_5.bungee.api.scheduler.TaskScheduler;
+import net.md_5.bungee.api.tab.CustomTabList;
+import net.md_5.bungee.command.CommandAlert;
+import net.md_5.bungee.command.CommandBungee;
+import net.md_5.bungee.command.CommandChangePassword;
+import net.md_5.bungee.command.CommandClearRatelimit;
+import net.md_5.bungee.command.CommandConfirmCode;
+import net.md_5.bungee.command.CommandDomain;
+import net.md_5.bungee.command.CommandDomainBlock;
+import net.md_5.bungee.command.CommandDomainBlockDomain;
+import net.md_5.bungee.command.CommandDomainUnblock;
+import net.md_5.bungee.command.CommandEnd;
+import net.md_5.bungee.command.CommandFind;
+import net.md_5.bungee.command.CommandGlobalBan;
+import net.md_5.bungee.command.CommandGlobalBanIP;
+import net.md_5.bungee.command.CommandGlobalBanRegex;
+import net.md_5.bungee.command.CommandGlobalBanReload;
+import net.md_5.bungee.command.CommandGlobalBanWildcard;
+import net.md_5.bungee.command.CommandGlobalCheckBan;
+import net.md_5.bungee.command.CommandGlobalListBan;
+import net.md_5.bungee.command.CommandGlobalUnban;
+import net.md_5.bungee.command.CommandIP;
+import net.md_5.bungee.command.CommandList;
+import net.md_5.bungee.command.CommandPerms;
+import net.md_5.bungee.command.CommandReload;
+import net.md_5.bungee.command.CommandSend;
+import net.md_5.bungee.command.CommandServer;
+import net.md_5.bungee.command.ConsoleCommandSender;
+import net.md_5.bungee.config.Configuration;
+import net.md_5.bungee.config.YamlConfig;
+import net.md_5.bungee.eaglercraft.AuthHandler;
+import net.md_5.bungee.eaglercraft.AuthSystem;
+import net.md_5.bungee.eaglercraft.BanList;
+import net.md_5.bungee.eaglercraft.DomainBlacklist;
+import net.md_5.bungee.eaglercraft.PluginEaglerSkins;
+import net.md_5.bungee.eaglercraft.PluginEaglerVoice;
+import net.md_5.bungee.eaglercraft.WebSocketListener;
+import net.md_5.bungee.log.BungeeLogger;
+import net.md_5.bungee.log.LoggingOutputStream;
+import net.md_5.bungee.netty.PipelineUtils;
+import net.md_5.bungee.protocol.packet.DefinedPacket;
+import net.md_5.bungee.protocol.packet.Packet3Chat;
+import net.md_5.bungee.protocol.packet.PacketFAPluginMessage;
+import net.md_5.bungee.reconnect.SQLReconnectHandler;
+import net.md_5.bungee.scheduler.BungeeScheduler;
+import net.md_5.bungee.scheduler.BungeeThreadPool;
+import net.md_5.bungee.tab.Custom;
+import net.md_5.bungee.util.CaseInsensitiveMap;
+
+public class BungeeCord extends ProxyServer {
+ public volatile boolean isRunning;
+ public final Configuration config;
+ public final ResourceBundle bundle;
+ public final ScheduledThreadPoolExecutor executors;
+ public final MultithreadEventLoopGroup eventLoops;
+ private final Timer saveThread;
+ private final Timer reloadBanThread;
+ private final Timer closeInactiveSockets;
+ private final Timer authTimeoutTimer;
+ private Collection listeners;
+ private Collection wsListeners;
+ private final Map connections;
+ private final ReadWriteLock connectionLock;
+ public final PluginManager pluginManager;
+ private ReconnectHandler reconnectHandler;
+ private ConfigurationAdapter configurationAdapter;
+ private final Collection pluginChannels;
+ private final File pluginsFolder;
+ private final TaskScheduler scheduler;
+ private ConsoleReader consoleReader;
+ private final Logger logger;
+ private Collection banCommands;
+ public AuthSystem authSystem;
+ public String tokenVerify;
+
+ public static BungeeCord getInstance() {
+ return (BungeeCord) ProxyServer.getInstance();
+ }
+
+ public BungeeCord() throws IOException {
+ this.config = new Configuration();
+ this.bundle = ResourceBundle.getBundle("messages_en");
+ this.executors = new BungeeThreadPool(new ThreadFactoryBuilder().setNameFormat("Bungee Pool Thread #%1$d").build());
+ this.eventLoops = (MultithreadEventLoopGroup) new NioEventLoopGroup(Runtime.getRuntime().availableProcessors(), new ThreadFactoryBuilder().setNameFormat("Netty IO Thread #%1$d").build());
+ this.saveThread = new Timer("Reconnect Saver");
+ this.reloadBanThread = new Timer("Ban List Reload");
+ this.closeInactiveSockets = new Timer("Close Inactive WebSockets");
+ this.authTimeoutTimer = new Timer("Auth Timeout");
+ this.listeners = new HashSet();
+ this.wsListeners = new HashSet();
+ this.connections = (Map) new CaseInsensitiveMap();
+ this.connectionLock = new ReentrantReadWriteLock();
+ this.pluginManager = new PluginManager(this);
+ this.configurationAdapter = new YamlConfig();
+ this.pluginChannels = new HashSet();
+ this.pluginsFolder = new File("plugins");
+ this.scheduler = new BungeeScheduler();
+ this.banCommands = new ArrayList();
+ this.getPluginManager().registerCommand(null, new CommandReload());
+ this.getPluginManager().registerCommand(null, new CommandEnd());
+ this.getPluginManager().registerCommand(null, new CommandList());
+ this.getPluginManager().registerCommand(null, new CommandServer());
+ this.getPluginManager().registerCommand(null, new CommandIP());
+ this.getPluginManager().registerCommand(null, new CommandAlert());
+ this.getPluginManager().registerCommand(null, new CommandBungee());
+ this.getPluginManager().registerCommand(null, new CommandPerms());
+ this.getPluginManager().registerCommand(null, new CommandSend());
+ this.getPluginManager().registerCommand(null, new CommandFind());
+ this.getPluginManager().registerCommand(null, new CommandClearRatelimit());
+ this.getPluginManager().registerCommand(null, new CommandConfirmCode());
+ this.getPluginManager().registerCommand(null, new CommandDomain());
+ this.getPluginManager().registerCommand(null, new CommandDomainBlock());
+ this.getPluginManager().registerCommand(null, new CommandDomainBlockDomain());
+ this.getPluginManager().registerCommand(null, new CommandDomainUnblock());
+ this.registerChannel("BungeeCord");
+ Log.setOutput(new PrintStream(ByteStreams.nullOutputStream()));
+ AnsiConsole.systemInstall();
+ this.consoleReader = new ConsoleReader();
+ this.logger = new BungeeLogger(this);
+ System.setErr(new PrintStream(new LoggingOutputStream(this.logger, Level.SEVERE), true));
+ System.setOut(new PrintStream(new LoggingOutputStream(this.logger, Level.INFO), true));
+ if (this.consoleReader.getTerminal() instanceof UnsupportedTerminal) {
+ this.logger.info("Unable to initialize fancy terminal. To fix this on Windows, install the correct Microsoft Visual C++ 2008 Runtime");
+ this.logger.info("NOTE: This error is non crucial, and BungeeCord will still function correctly! Do not bug the author about it unless you are still unable to get it working");
+ }
+ }
+
+ public void reconfigureBanCommands(boolean replaceBukkit) {
+ if(banCommands.size() > 0) {
+ for(Command c : banCommands) {
+ this.getPluginManager().unregisterCommand(c);
+ }
+ banCommands.clear();
+ }
+
+ Command cBan = new CommandGlobalBan(replaceBukkit);
+ Command cUnban = new CommandGlobalUnban(replaceBukkit);
+ Command cBanReload = new CommandGlobalBanReload(replaceBukkit);
+ Command cBanIP = new CommandGlobalBanIP(replaceBukkit);
+ Command cBanWildcard = new CommandGlobalBanWildcard(replaceBukkit);
+ Command cBanRegex = new CommandGlobalBanRegex(replaceBukkit);
+ Command cBanCheck = new CommandGlobalCheckBan(replaceBukkit);
+ Command cBanList = new CommandGlobalListBan(replaceBukkit);
+
+ banCommands.add(cBan);
+ banCommands.add(cUnban);
+ banCommands.add(cBanReload);
+ banCommands.add(cBanIP);
+ banCommands.add(cBanWildcard);
+ banCommands.add(cBanRegex);
+ banCommands.add(cBanCheck);
+ banCommands.add(cBanList);
+
+ this.getPluginManager().registerCommand(null, cBan);
+ this.getPluginManager().registerCommand(null, cUnban);
+ this.getPluginManager().registerCommand(null, cBanReload);
+ this.getPluginManager().registerCommand(null, cBanIP);
+ this.getPluginManager().registerCommand(null, cBanWildcard);
+ this.getPluginManager().registerCommand(null, cBanRegex);
+ this.getPluginManager().registerCommand(null, cBanCheck);
+ this.getPluginManager().registerCommand(null, cBanList);
+ }
+
+ public static void main(final String[] args) throws Exception {
+ final BungeeCord bungee = new BungeeCord();
+ ProxyServer.setInstance(bungee);
+ bungee.getLogger().info("Enabled BungeeCord version " + bungee.getVersion());
+ bungee.start();
+ while (bungee.isRunning) {
+ final String line = bungee.getConsoleReader().readLine(">");
+ if (line != null && !bungee.getPluginManager().dispatchCommand(ConsoleCommandSender.getInstance(), line)) {
+ bungee.getConsole().sendMessage(ChatColor.RED + "Command not found");
+ }
+ }
+ }
+
+ @Override
+ public void start() throws Exception {
+ this.pluginsFolder.mkdir();
+ this.config.load();
+ this.pluginManager.detectPlugins(this.pluginsFolder);
+ this.pluginManager.addInternalPlugin(new PluginEaglerSkins());
+ this.pluginManager.addInternalPlugin(new PluginEaglerVoice(this.config.getVoiceEnabled()));
+ if (this.config.getAuthInfo().isEnabled()) {
+ this.authSystem = new AuthSystem(this.config.getAuthInfo());
+ this.getPluginManager().registerCommand(null, new CommandChangePassword(this.authSystem));
+ }
+ this.tokenVerify = Optional.ofNullable(System.getenv("YEEISH_TOKEN")).orElse(this.config.getTokenVerify());
+ if (this.reconnectHandler == null) {
+ this.reconnectHandler = new SQLReconnectHandler();
+ }
+ this.isRunning = true;
+ this.pluginManager.loadAndEnablePlugins();
+ this.startListeners();
+ this.saveThread.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ BungeeCord.this.getReconnectHandler().save();
+ }
+ }, 0L, TimeUnit.MINUTES.toMillis(5L));
+ this.reloadBanThread.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ BanList.maybeReloadBans(null);
+ }
+ }, 0L, TimeUnit.SECONDS.toMillis(3L));
+ DomainBlacklist.init(this);
+ this.closeInactiveSockets.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ DomainBlacklist.update();
+ for(WebSocketListener lst : BungeeCord.this.wsListeners) {
+ lst.closeInactiveSockets();
+ ListenerInfo info = lst.getInfo();
+ if(info.getRateLimitIP() != null) info.getRateLimitIP().deleteClearLimiters();
+ if(info.getRateLimitLogin() != null) info.getRateLimitLogin().deleteClearLimiters();
+ if(info.getRateLimitMOTD() != null) info.getRateLimitMOTD().deleteClearLimiters();
+ if(info.getRateLimitQuery() != null) info.getRateLimitQuery().deleteClearLimiters();
+ }
+ }
+ }, 0L, TimeUnit.SECONDS.toMillis(10L));
+ final int authTimeout = this.config.getAuthInfo().getLoginTimeout();
+ this.authTimeoutTimer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ AuthHandler.closeInactive(authTimeout);
+ }
+ }, 0L, TimeUnit.SECONDS.toMillis(2L));
+ }
+
+ public void startListeners() {
+ for (final ListenerInfo info : this.config.getListeners()) {
+ InetSocketAddress sock = info.getHost();
+ if(info.isWebsocket()) {
+ sock = info.getJavaHost();
+ if(sock == null) {
+ try {
+ ServerSocket s = new ServerSocket(0, 0, InetAddress.getByName("127.11.0.1"));
+ sock = new InetSocketAddress("127.11.0.1", s.getLocalPort());
+ s.close();
+ } catch(IOException e) {
+ sock = new InetSocketAddress("127.11.0.1",(int) (System.nanoTime() % 64000L + 1025L));
+ }
+ }
+ try {
+ this.wsListeners.add(new WebSocketListener(info, sock, this));
+ BungeeCord.this.getLogger().info("Listening websockets on " + info.getHost());
+ }catch(Throwable t) {
+ BungeeCord.this.getLogger().log(Level.WARNING, "Could not bind websocket listener to host " + info.getHost(), t);
+ }
+ }
+ final InetSocketAddress sock2 = sock;
+ final ChannelFutureListener listener = (ChannelFutureListener) new ChannelFutureListener() {
+ public void operationComplete(final ChannelFuture future) throws Exception {
+ if (future.isSuccess()) {
+ BungeeCord.this.listeners.add(future.channel());
+ BungeeCord.this.getLogger().info("Listening on " + sock2);
+ } else {
+ BungeeCord.this.getLogger().log(Level.WARNING, "Could not bind to host " + sock2, future.cause());
+ }
+ }
+ };
+ ((ServerBootstrap) ((ServerBootstrap) new ServerBootstrap().channel((Class) NioServerSocketChannel.class)).childAttr((AttributeKey) PipelineUtils.LISTENER, (Object) info).childHandler((ChannelHandler) PipelineUtils.SERVER_CHILD)
+ .group((EventLoopGroup) this.eventLoops).localAddress((SocketAddress) sock)).bind().addListener((GenericFutureListener) listener);
+ }
+ }
+
+ public void stopListeners() {
+ for (final Channel listener : this.listeners) {
+ this.getLogger().log(Level.INFO, "Closing listener {0}", listener);
+ try {
+ listener.close().syncUninterruptibly();
+ } catch (ChannelException ex) {
+ this.getLogger().severe("Could not close listen thread");
+ }
+ }
+ for (final WebSocketListener listener : this.wsListeners) {
+ this.getLogger().log(Level.INFO, "Closing websocket listener {0}", listener.getAddress());
+ try {
+ listener.stop();
+ }catch (IOException e) {
+ this.getLogger().severe("Could not close listen thread");
+ e.printStackTrace();
+ } catch (InterruptedException e) {
+ this.getLogger().severe("Could not close listen thread");
+ e.printStackTrace();
+ }
+ }
+ this.listeners.clear();
+ }
+
+ @Override
+ public void stop() {
+ new Thread("Shutdown Thread") {
+ @Override
+ public void run() {
+ BungeeCord.this.isRunning = false;
+ BungeeCord.this.executors.shutdown();
+ BungeeCord.this.stopListeners();
+ BungeeCord.this.getLogger().info("Closing pending connections");
+ BungeeCord.this.connectionLock.readLock().lock();
+ try {
+ BungeeCord.this.getLogger().info("Disconnecting " + BungeeCord.this.connections.size() + " connections");
+ for (final UserConnection user : BungeeCord.this.connections.values()) {
+ user.disconnect(BungeeCord.this.getTranslation("restart"));
+ }
+ } finally {
+ BungeeCord.this.connectionLock.readLock().unlock();
+ }
+ BungeeCord.this.getLogger().info("Closing IO threads");
+ BungeeCord.this.eventLoops.shutdownGracefully();
+ try {
+ BungeeCord.this.eventLoops.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
+ } catch (InterruptedException ex) {
+ }
+ BungeeCord.this.getLogger().info("Saving reconnect locations");
+ BungeeCord.this.reconnectHandler.save();
+ BungeeCord.this.reconnectHandler.close();
+ BungeeCord.this.saveThread.cancel();
+ BungeeCord.this.reloadBanThread.cancel();
+ BungeeCord.this.closeInactiveSockets.cancel();
+ BungeeCord.this.authTimeoutTimer.cancel();
+ BungeeCord.this.getLogger().info("Disabling plugins");
+ for (final Plugin plugin : BungeeCord.this.pluginManager.getPlugins()) {
+ plugin.onDisable();
+ BungeeCord.this.getScheduler().cancel(plugin);
+ }
+ BungeeCord.this.getLogger().info("Thankyou and goodbye");
+ System.exit(0);
+ }
+ }.start();
+ }
+
+ public void broadcast(final DefinedPacket packet) {
+ this.connectionLock.readLock().lock();
+ try {
+ for (final UserConnection con : this.connections.values()) {
+ con.unsafe().sendPacket(packet);
+ }
+ } finally {
+ this.connectionLock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public String getName() {
+ return "BungeeCord";
+ }
+
+ @Override
+ public String getVersion() {
+ return (BungeeCord.class.getPackage().getImplementationVersion() == null) ? "unknown" : BungeeCord.class.getPackage().getImplementationVersion();
+ }
+
+ @Override
+ public String getTranslation(final String name, Object... args) {
+ String translation = "";
+ try {
+ translation = this.bundle.getString(name);
+ } catch (MissingResourceException ex) {
+ }
+ return translation;
+ }
+
+ @Override
+ public Collection getPlayers() {
+ this.connectionLock.readLock().lock();
+ try {
+ return new HashSet(this.connections.values());
+ } finally {
+ this.connectionLock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public int getOnlineCount() {
+ return this.connections.size();
+ }
+
+ @Override
+ public ProxiedPlayer getPlayer(final String name) {
+ this.connectionLock.readLock().lock();
+ try {
+ return this.connections.get(name);
+ } finally {
+ this.connectionLock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public Map getServers() {
+ return (Map) this.config.getServers();
+ }
+
+ @Override
+ public ServerInfo getServerInfo(final String name) {
+ return this.getServers().get(name);
+ }
+
+ @Override
+ public void registerChannel(final String channel) {
+ synchronized (this.pluginChannels) {
+ this.pluginChannels.add(channel);
+ }
+ }
+
+ @Override
+ public void unregisterChannel(final String channel) {
+ synchronized (this.pluginChannels) {
+ this.pluginChannels.remove(channel);
+ }
+ }
+
+ @Override
+ public Collection getChannels() {
+ synchronized (this.pluginChannels) {
+ return Collections.unmodifiableCollection((Collection extends String>) this.pluginChannels);
+ }
+ }
+
+ public PacketFAPluginMessage registerChannels() {
+ return new PacketFAPluginMessage("REGISTER", Util.format(this.pluginChannels, "\u0000").getBytes());
+ }
+
+ @Override
+ public byte getProtocolVersion() {
+ return 78;
+ }
+
+ @Override
+ public String getGameVersion() {
+ return "1.6.4";
+ }
+
+ @Override
+ public ServerInfo constructServerInfo(final String name, final InetSocketAddress address, final boolean restricted) {
+ return new BungeeServerInfo(name, address, restricted);
+ }
+
+ @Override
+ public CommandSender getConsole() {
+ return ConsoleCommandSender.getInstance();
+ }
+
+ @Override
+ public void broadcast(final String message) {
+ this.getConsole().sendMessage(message);
+ this.broadcast(new Packet3Chat(message));
+ }
+
+ public void addConnection(final UserConnection con) {
+ this.connectionLock.writeLock().lock();
+ try {
+ this.connections.put(con.getName(), con);
+ } finally {
+ this.connectionLock.writeLock().unlock();
+ }
+ }
+
+ public void removeConnection(final UserConnection con) {
+ this.connectionLock.writeLock().lock();
+ try {
+ this.connections.remove(con.getName());
+ } finally {
+ this.connectionLock.writeLock().unlock();
+ }
+ }
+
+ @Override
+ public CustomTabList customTabList(final ProxiedPlayer player) {
+ return new Custom(player);
+ }
+
+ @Override
+ public Collection getDisabledCommands() {
+ return List.of();
+ }
+
+ @Override
+ public PluginManager getPluginManager() {
+ return this.pluginManager;
+ }
+
+ @Override
+ public ReconnectHandler getReconnectHandler() {
+ return this.reconnectHandler;
+ }
+
+ @Override
+ public void setReconnectHandler(final ReconnectHandler reconnectHandler) {
+ this.reconnectHandler = reconnectHandler;
+ }
+
+ @Override
+ public ConfigurationAdapter getConfigurationAdapter() {
+ return this.configurationAdapter;
+ }
+
+ @Override
+ public void setConfigurationAdapter(final ConfigurationAdapter configurationAdapter) {
+ this.configurationAdapter = configurationAdapter;
+ }
+
+ @Override
+ public File getPluginsFolder() {
+ return this.pluginsFolder;
+ }
+
+ @Override
+ public TaskScheduler getScheduler() {
+ return this.scheduler;
+ }
+
+ public ConsoleReader getConsoleReader() {
+ return this.consoleReader;
+ }
+
+ @Override
+ public Logger getLogger() {
+ return this.logger;
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/BungeeServerInfo.java b/eaglerbungee/src/main/java/net/md_5/bungee/BungeeServerInfo.java
new file mode 100644
index 0000000..82dd39d
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/BungeeServerInfo.java
@@ -0,0 +1,142 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee;
+
+import java.beans.ConstructorProperties;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.Objects;
+import java.util.Queue;
+
+import com.google.common.base.Preconditions;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.util.concurrent.GenericFutureListener;
+import net.md_5.bungee.api.Callback;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.ServerPing;
+import net.md_5.bungee.api.config.ServerInfo;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.connection.Server;
+import net.md_5.bungee.connection.PingHandler;
+import net.md_5.bungee.netty.HandlerBoss;
+import net.md_5.bungee.netty.PipelineUtils;
+import net.md_5.bungee.protocol.packet.DefinedPacket;
+import net.md_5.bungee.protocol.packet.PacketFAPluginMessage;
+
+public class BungeeServerInfo implements ServerInfo {
+ private final String name;
+ private final InetSocketAddress address;
+ private final Collection players;
+ private final boolean restricted;
+ private final Queue packetQueue;
+
+ public void addPlayer(final ProxiedPlayer player) {
+ synchronized (this.players) {
+ this.players.add(player);
+ }
+ }
+
+ public void removePlayer(final ProxiedPlayer player) {
+ synchronized (this.players) {
+ this.players.remove(player);
+ }
+ }
+
+ @Override
+ public Collection getPlayers() {
+ synchronized (this.players) {
+ return Collections.unmodifiableCollection((Collection extends ProxiedPlayer>) this.players);
+ }
+ }
+
+ @Override
+ public boolean canAccess(final CommandSender player) {
+ Preconditions.checkNotNull((Object) player, (Object) "player");
+ return !this.restricted || player.hasPermission("bungeecord.server." + this.name);
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ return obj instanceof ServerInfo && Objects.equals(this.getAddress(), ((ServerInfo) obj).getAddress());
+ }
+
+ @Override
+ public int hashCode() {
+ return this.address.hashCode();
+ }
+
+ @Override
+ public void sendData(final String channel, final byte[] data) {
+ Preconditions.checkNotNull((Object) channel, (Object) "channel");
+ Preconditions.checkNotNull((Object) data, (Object) "data");
+ final Server server = this.players.isEmpty() ? null : this.players.iterator().next().getServer();
+ if (server != null) {
+ server.sendData(channel, data);
+ } else {
+ synchronized (this.packetQueue) {
+ this.packetQueue.add(new PacketFAPluginMessage(channel, data));
+ }
+ }
+ }
+
+ @Override
+ public void ping(final Callback callback) {
+ Preconditions.checkNotNull((Object) callback, (Object) "callback");
+ final ChannelFutureListener listener = (ChannelFutureListener) new ChannelFutureListener() {
+ public void operationComplete(final ChannelFuture future) throws Exception {
+ if (future.isSuccess()) {
+ ((HandlerBoss) future.channel().pipeline().get((Class) HandlerBoss.class)).setHandler(new PingHandler(BungeeServerInfo.this, callback));
+ } else {
+ callback.done(null, future.cause());
+ }
+ }
+ };
+ ((Bootstrap) ((Bootstrap) ((Bootstrap) ((Bootstrap) new Bootstrap().channel((Class) NioSocketChannel.class)).group((EventLoopGroup) BungeeCord.getInstance().eventLoops)).handler((ChannelHandler) PipelineUtils.BASE))
+ .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, Integer.valueOf(5000))).remoteAddress((SocketAddress) this.getAddress()).connect().addListener((GenericFutureListener) listener);
+ }
+
+ @ConstructorProperties({ "name", "address", "restricted" })
+ public BungeeServerInfo(final String name, final InetSocketAddress address, final boolean restricted) {
+ this.players = new ArrayList();
+ this.packetQueue = new LinkedList();
+ this.name = name;
+ this.address = address;
+ this.restricted = restricted;
+ }
+
+ @Override
+ public String getName() {
+ return this.name;
+ }
+
+ @Override
+ public InetSocketAddress getAddress() {
+ return this.address;
+ }
+
+ public boolean isRestricted() {
+ return this.restricted;
+ }
+
+ public Queue getPacketQueue() {
+ return this.packetQueue;
+ }
+
+ @Override
+ public String getRedirect() {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/ConnectionThrottle.java b/eaglerbungee/src/main/java/net/md_5/bungee/ConnectionThrottle.java
new file mode 100644
index 0000000..fe9660c
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/ConnectionThrottle.java
@@ -0,0 +1,30 @@
+package net.md_5.bungee;
+
+import java.net.InetAddress;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ConnectionThrottle
+{
+
+ private final Map throttle = new HashMap<>();
+ private final int throttleTime;
+
+ public ConnectionThrottle(int throttleTime) {
+ this.throttleTime = throttleTime;
+ }
+
+ public void unthrottle(InetAddress address)
+ {
+ throttle.remove( address );
+ }
+
+ public boolean throttle(InetAddress address)
+ {
+ Long value = throttle.get( address );
+ long currentTime = System.currentTimeMillis();
+
+ throttle.put( address, currentTime );
+ return value != null && currentTime - value < throttleTime;
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/EncryptionUtil.java b/eaglerbungee/src/main/java/net/md_5/bungee/EncryptionUtil.java
new file mode 100644
index 0000000..c3d5ef0
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/EncryptionUtil.java
@@ -0,0 +1,88 @@
+package net.md_5.bungee;
+
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Arrays;
+import java.util.Random;
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import net.md_5.bungee.protocol.packet.PacketFCEncryptionResponse;
+import net.md_5.bungee.protocol.packet.PacketFDEncryptionRequest;
+
+/**
+ * Class containing all encryption related methods for the proxy.
+ */
+public class EncryptionUtil
+{
+
+ private static final Random random = new Random();
+ public static KeyPair keys;
+ private static SecretKey secret = new SecretKeySpec( new byte[ 16 ], "AES" );
+
+
+ static
+ {
+ try
+ {
+ keys = KeyPairGenerator.getInstance( "RSA" ).generateKeyPair();
+ } catch ( NoSuchAlgorithmException ex )
+ {
+ throw new ExceptionInInitializerError( ex );
+ }
+ }
+
+ public static PacketFDEncryptionRequest encryptRequest()
+ {
+ String hash = "-";
+ byte[] pubKey = keys.getPublic().getEncoded();
+ byte[] verify = new byte[ 4 ];
+ random.nextBytes( verify );
+ return new PacketFDEncryptionRequest( hash, pubKey, verify );
+ }
+
+ public static SecretKey getSecret() {
+ return EncryptionUtil.secret;
+ }
+
+ public static SecretKey getSecret(PacketFCEncryptionResponse resp, PacketFDEncryptionRequest request) throws GeneralSecurityException
+ {
+ Cipher cipher = Cipher.getInstance( "RSA" );
+ cipher.init( Cipher.DECRYPT_MODE, keys.getPrivate() );
+ byte[] decrypted = cipher.doFinal( resp.getVerifyToken() );
+
+ if ( !Arrays.equals( request.getVerifyToken(), decrypted ) )
+ {
+ throw new IllegalStateException( "Key pairs do not match!" );
+ }
+
+ cipher.init( Cipher.DECRYPT_MODE, keys.getPrivate() );
+ return new SecretKeySpec( cipher.doFinal( resp.getSharedSecret() ), "AES" );
+ }
+
+ public static Cipher getCipher(int opMode, Key shared) throws GeneralSecurityException
+ {
+ Cipher cip = Cipher.getInstance( "AES/CFB8/NoPadding" );
+ cip.init( opMode, shared, new IvParameterSpec( shared.getEncoded() ) );
+ return cip;
+ }
+
+ public static PublicKey getPubkey(PacketFDEncryptionRequest request) throws GeneralSecurityException
+ {
+ return KeyFactory.getInstance( "RSA" ).generatePublic( new X509EncodedKeySpec( request.getPublicKey() ) );
+ }
+
+ public static byte[] encrypt(Key key, byte[] b) throws GeneralSecurityException
+ {
+ Cipher hasher = Cipher.getInstance( "RSA" );
+ hasher.init( Cipher.ENCRYPT_MODE, key );
+ return hasher.doFinal( b );
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/EntityMap.java b/eaglerbungee/src/main/java/net/md_5/bungee/EntityMap.java
new file mode 100644
index 0000000..679bbaf
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/EntityMap.java
@@ -0,0 +1,169 @@
+package net.md_5.bungee;
+
+import io.netty.buffer.ByteBuf;
+
+/**
+ * Class to rewrite integers within packets.
+ */
+public class EntityMap
+{
+
+ public final static int[][] entityIds = new int[ 256 ][];
+
+ static
+ {
+ entityIds[0x05] = new int[]
+ {
+ 1
+ };
+ entityIds[0x07] = new int[]
+ {
+ 1, 5
+ };
+ entityIds[0x11] = new int[]
+ {
+ 1
+ };
+ entityIds[0x12] = new int[]
+ {
+ 1
+ };
+ entityIds[0x13] = new int[]
+ {
+ 1
+ };
+ entityIds[0x14] = new int[]
+ {
+ 1
+ };
+ entityIds[0x16] = new int[]
+ {
+ 1, 5
+ };
+ entityIds[0x17] = new int[]
+ {
+ 1 //, 20
+ };
+ entityIds[0x18] = new int[]
+ {
+ 1
+ };
+ entityIds[0x19] = new int[]
+ {
+ 1
+ };
+ entityIds[0x1A] = new int[]
+ {
+ 1
+ };
+ entityIds[0x1C] = new int[]
+ {
+ 1
+ };
+ entityIds[0x1E] = new int[]
+ {
+ 1
+ };
+ entityIds[0x1F] = new int[]
+ {
+ 1
+ };
+ entityIds[0x20] = new int[]
+ {
+ 1
+ };
+ entityIds[0x21] = new int[]
+ {
+ 1
+ };
+ entityIds[0x22] = new int[]
+ {
+ 1
+ };
+ entityIds[0x23] = new int[]
+ {
+ 1
+ };
+ entityIds[0x26] = new int[]
+ {
+ 1
+ };
+ entityIds[0x27] = new int[]
+ {
+ 1, 5
+ };
+ entityIds[0x28] = new int[]
+ {
+ 1
+ };
+ entityIds[0x29] = new int[]
+ {
+ 1
+ };
+ entityIds[0x2A] = new int[]
+ {
+ 1
+ };
+ entityIds[0x2C] = new int[]
+ {
+ 1
+ };
+ entityIds[0x37] = new int[]
+ {
+ 1
+ };
+
+ entityIds[0x47] = new int[]
+ {
+ 1
+ };
+ }
+
+ public static void rewrite(ByteBuf packet, int oldId, int newId)
+ {
+ int packetId = packet.getUnsignedByte( 0 );
+ if ( packetId == 0x1D )
+ { // bulk entity
+ for ( int pos = 2; pos < packet.readableBytes(); pos += 4 )
+ {
+ int readId = packet.getInt( pos );
+ if ( readId == oldId )
+ {
+ packet.setInt( pos, newId );
+ } else if ( readId == newId )
+ {
+ packet.setInt( pos, oldId );
+ }
+ }
+ } else
+ {
+ int[] idArray = entityIds[packetId];
+ if ( idArray != null )
+ {
+ for ( int pos : idArray )
+ {
+ int readId = packet.getInt( pos );
+ if ( readId == oldId )
+ {
+ packet.setInt( pos, newId );
+ } else if ( readId == newId )
+ {
+ packet.setInt( pos, oldId );
+ }
+ }
+ }
+ }
+ if ( packetId == 0x17 )
+ {
+ int type = packet.getUnsignedByte( 5 );
+ if ( type == 60 || type == 90 )
+ {
+ int index20 = packet.getInt( 20 );
+ if ( packet.readableBytes() > 24 && index20 == oldId )
+ {
+ packet.setInt( 20, newId );
+ }
+ }
+ }
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/PacketConstants.java b/eaglerbungee/src/main/java/net/md_5/bungee/PacketConstants.java
new file mode 100644
index 0000000..e63212d
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/PacketConstants.java
@@ -0,0 +1,18 @@
+package net.md_5.bungee;
+
+import net.md_5.bungee.protocol.packet.Packet9Respawn;
+import net.md_5.bungee.protocol.packet.PacketCDClientStatus;
+import net.md_5.bungee.protocol.packet.PacketFAPluginMessage;
+
+public class PacketConstants
+{
+
+ public static final Packet9Respawn DIM1_SWITCH = new Packet9Respawn( (byte) 1, (byte) 0, (byte) 0, (short) 256, "DEFAULT" );
+ public static final Packet9Respawn DIM2_SWITCH = new Packet9Respawn( (byte) -1, (byte) 0, (byte) 0, (short) 256, "DEFAULT" );
+ public static final PacketCDClientStatus CLIENT_LOGIN = new PacketCDClientStatus( (byte) 0 );
+ public static final PacketFAPluginMessage FORGE_MOD_REQUEST = new PacketFAPluginMessage( "FML", new byte[]
+ {
+ 0, 0, 0, 0, 0, 2
+ } );
+ public static final PacketFAPluginMessage I_AM_BUNGEE = new PacketFAPluginMessage( "BungeeCord", new byte[ 0 ] );
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/ServerConnection.java b/eaglerbungee/src/main/java/net/md_5/bungee/ServerConnection.java
new file mode 100644
index 0000000..2e0435b
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/ServerConnection.java
@@ -0,0 +1,94 @@
+package net.md_5.bungee;
+
+import java.beans.ConstructorProperties;
+import java.net.InetSocketAddress;
+import java.util.concurrent.TimeUnit;
+
+import net.md_5.bungee.api.connection.Connection;
+import net.md_5.bungee.api.connection.Server;
+import net.md_5.bungee.netty.ChannelWrapper;
+import net.md_5.bungee.protocol.packet.DefinedPacket;
+import net.md_5.bungee.protocol.packet.PacketFAPluginMessage;
+import net.md_5.bungee.protocol.packet.PacketFFKick;
+
+public class ServerConnection implements Server
+{
+
+ private final ChannelWrapper ch;
+ private final BungeeServerInfo info;
+ private boolean isObsolete;
+
+ private Unsafe unsafe = new Unsafe()
+ {
+ @Override
+ public void sendPacket(DefinedPacket packet)
+ {
+ ch.write( packet );
+ }
+ };
+
+ @Override
+ public void sendData(String channel, byte[] data)
+ {
+ unsafe().sendPacket( new PacketFAPluginMessage( channel, data ) );
+ }
+
+ @Override
+ public synchronized void disconnect(String reason)
+ {
+ if ( !ch.isClosed() )
+ {
+ // TODO: Can we just use a future here?
+ unsafe().sendPacket( new PacketFFKick( reason ) );
+ ch.getHandle().eventLoop().schedule( new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ ch.getHandle().close();
+ }
+ }, 100, TimeUnit.MILLISECONDS );
+ }
+ }
+
+ @Override
+ public InetSocketAddress getAddress()
+ {
+ return getInfo().getAddress();
+ }
+
+ @Override
+ public Unsafe unsafe()
+ {
+ return unsafe;
+ }
+
+ @ConstructorProperties({ "ch", "info" })
+ public ServerConnection(final ChannelWrapper ch, final BungeeServerInfo info) {
+ this.unsafe = new Connection.Unsafe() {
+ @Override
+ public void sendPacket(final DefinedPacket packet) {
+ if (ServerConnection.this.ch != null) ServerConnection.this.ch.write(packet);
+ }
+ };
+ this.ch = ch;
+ this.info = info;
+ }
+
+ public ChannelWrapper getCh() {
+ return this.ch;
+ }
+
+ @Override
+ public BungeeServerInfo getInfo() {
+ return this.info;
+ }
+
+ public boolean isObsolete() {
+ return this.isObsolete;
+ }
+
+ public void setObsolete(final boolean isObsolete) {
+ this.isObsolete = isObsolete;
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/ServerConnector.java b/eaglerbungee/src/main/java/net/md_5/bungee/ServerConnector.java
new file mode 100644
index 0000000..8b6fdcd
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/ServerConnector.java
@@ -0,0 +1,309 @@
+package net.md_5.bungee;
+
+import com.google.common.base.Preconditions;
+import com.google.common.io.ByteArrayDataInput;
+import com.google.common.io.ByteArrayDataOutput;
+import com.google.common.io.ByteStreams;
+import java.io.DataInput;
+import java.security.PublicKey;
+import java.util.Objects;
+import java.util.Queue;
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.config.ServerInfo;
+import net.md_5.bungee.api.event.ServerConnectedEvent;
+import net.md_5.bungee.api.event.ServerKickEvent;
+import net.md_5.bungee.api.event.ServerSwitchEvent;
+import net.md_5.bungee.api.score.Objective;
+import net.md_5.bungee.api.score.Scoreboard;
+import net.md_5.bungee.api.score.Team;
+import net.md_5.bungee.connection.CancelSendSignal;
+import net.md_5.bungee.connection.DownstreamBridge;
+import net.md_5.bungee.netty.HandlerBoss;
+import net.md_5.bungee.netty.ChannelWrapper;
+import net.md_5.bungee.netty.CipherDecoder;
+import net.md_5.bungee.netty.CipherEncoder;
+import net.md_5.bungee.netty.PacketDecoder;
+import net.md_5.bungee.netty.PacketHandler;
+import net.md_5.bungee.netty.PipelineUtils;
+import net.md_5.bungee.protocol.Forge;
+import net.md_5.bungee.protocol.MinecraftOutput;
+import net.md_5.bungee.protocol.packet.DefinedPacket;
+import net.md_5.bungee.protocol.packet.Packet1Login;
+import net.md_5.bungee.protocol.packet.Packet9Respawn;
+import net.md_5.bungee.protocol.packet.PacketCEScoreboardObjective;
+import net.md_5.bungee.protocol.packet.PacketD1Team;
+import net.md_5.bungee.protocol.packet.PacketFAPluginMessage;
+import net.md_5.bungee.protocol.packet.PacketFCEncryptionResponse;
+import net.md_5.bungee.protocol.packet.PacketFDEncryptionRequest;
+import net.md_5.bungee.protocol.packet.PacketFFKick;
+import net.md_5.bungee.protocol.packet.forge.Forge1Login;
+
+public class ServerConnector extends PacketHandler
+{
+ private final ProxyServer bungee;
+ private ChannelWrapper ch;
+ private final UserConnection user;
+ private final BungeeServerInfo target;
+ private State thisState = State.ENCRYPT_REQUEST;
+ private SecretKey secretkey;
+ private boolean sentMessages;
+
+ public ServerConnector(ProxyServer bungee, UserConnection user, BungeeServerInfo target){
+ this.bungee = bungee;
+ this.user = user;
+ this.target = target;
+ }
+
+ private enum State
+ {
+
+ ENCRYPT_REQUEST, ENCRYPT_RESPONSE, LOGIN, FINISHED;
+ }
+
+ @Override
+ public void exception(Throwable t) throws Exception
+ {
+ String message = "Exception Connecting:" + Util.exception( t );
+ if ( user.getServer() == null )
+ {
+ user.disconnect( message );
+ } else
+ {
+ user.sendMessage( ChatColor.RED + message );
+ }
+ }
+
+ @Override
+ public void connected(ChannelWrapper channel) throws Exception
+ {
+ this.ch = channel;
+
+ ByteArrayDataOutput out = ByteStreams.newDataOutput();
+ out.writeUTF( "Login" );
+ out.writeUTF( user.getAddress().getHostString() );
+ out.writeInt( user.getAddress().getPort() );
+ channel.write( new PacketFAPluginMessage( "BungeeCord", out.toByteArray() ) );
+
+ channel.write( user.getPendingConnection().getHandshake() );
+
+ // Skip encryption if we are not using Forge
+ if ( user.getPendingConnection().getForgeLogin() == null )
+ {
+ channel.write( PacketConstants.CLIENT_LOGIN );
+ }
+ }
+
+ @Override
+ public void disconnected(ChannelWrapper channel) throws Exception
+ {
+ user.getPendingConnects().remove( target );
+ }
+
+ @Override
+ public void handle(Packet1Login login) throws Exception
+ {
+ Preconditions.checkState( thisState == State.LOGIN, "Not exepcting LOGIN" );
+
+ ServerConnection server = new ServerConnection( ch, target );
+ ServerConnectedEvent event = new ServerConnectedEvent( user, server );
+ bungee.getPluginManager().callEvent( event );
+
+ ch.write( BungeeCord.getInstance().registerChannels() );
+ Queue packetQueue = target.getPacketQueue();
+ synchronized ( packetQueue )
+ {
+ while ( !packetQueue.isEmpty() )
+ {
+ ch.write( packetQueue.poll() );
+ }
+ }
+
+ for ( PacketFAPluginMessage message : user.getPendingConnection().getRegisterMessages() )
+ {
+ ch.write( message );
+ }
+ if ( !sentMessages )
+ {
+ for ( PacketFAPluginMessage message : user.getPendingConnection().getLoginMessages() )
+ {
+ ch.write( message );
+ }
+ }
+
+ if ( user.getSettings() != null )
+ {
+ ch.write( user.getSettings() );
+ }
+
+ synchronized ( user.getSwitchMutex() )
+ {
+ if ( user.getServer() == null )
+ {
+ // Once again, first connection
+ user.setClientEntityId( login.getEntityId() );
+ user.setServerEntityId( login.getEntityId() );
+
+ // Set tab list size, this sucks balls, TODO: what shall we do about packet mutability
+ Packet1Login modLogin;
+ if ( ch.getHandle().pipeline().get( PacketDecoder.class ).getProtocol() == Forge.getInstance() )
+ {
+ modLogin = new Forge1Login( login.getEntityId(), login.getLevelType(), login.getGameMode(), login.getDimension(), login.getDifficulty(), login.getUnused(),
+ (byte) user.getPendingConnection().getListener().getTabListSize() );
+ } else
+ {
+ modLogin = new Packet1Login( login.getEntityId(), login.getLevelType(), login.getGameMode(), (byte) login.getDimension(), login.getDifficulty(), login.getUnused(),
+ (byte) user.getPendingConnection().getListener().getTabListSize() );
+ }
+ user.unsafe().sendPacket( modLogin );
+
+ MinecraftOutput out = new MinecraftOutput();
+ out.writeStringUTF8WithoutLengthHeaderBecauseDinnerboneStuffedUpTheMCBrandPacket( ProxyServer.getInstance().getName() + " (" + ProxyServer.getInstance().getVersion() + ")" );
+ user.unsafe().sendPacket( new PacketFAPluginMessage( "MC|Brand", out.toArray() ) );
+ } else
+ {
+ user.getTabList().onServerChange();
+
+ Scoreboard serverScoreboard = user.getServerSentScoreboard();
+ for ( Objective objective : serverScoreboard.getObjectives() )
+ {
+ user.unsafe().sendPacket( new PacketCEScoreboardObjective( objective.getName(), objective.getValue(), (byte) 1 ) );
+ }
+ for ( Team team : serverScoreboard.getTeams() )
+ {
+ user.unsafe().sendPacket( new PacketD1Team( team.getName() ) );
+ }
+ serverScoreboard.clear();
+
+ user.sendDimensionSwitch();
+
+ user.setServerEntityId( login.getEntityId() );
+ user.unsafe().sendPacket( new Packet9Respawn( login.getDimension(), login.getDifficulty(), login.getGameMode(), (short) 256, login.getLevelType() ) );
+
+ // Remove from old servers
+ user.getServer().setObsolete( true );
+ user.getServer().disconnect( "Quitting" );
+ }
+
+ // TODO: Fix this?
+ if ( !user.isActive() )
+ {
+ server.disconnect( "Quitting" );
+ // Silly server admins see stack trace and die
+ bungee.getLogger().warning( "No client connected for pending server!" );
+ return;
+ }
+
+ // Add to new server
+ // TODO: Move this to the connected() method of DownstreamBridge
+ target.addPlayer( user );
+ user.getPendingConnects().remove( target );
+
+ user.setServer( server );
+ ch.getHandle().pipeline().get( HandlerBoss.class ).setHandler( new DownstreamBridge( bungee, user, server ) );
+ }
+
+ bungee.getPluginManager().callEvent( new ServerSwitchEvent( user ) );
+
+ thisState = State.FINISHED;
+
+ throw new CancelSendSignal();
+ }
+
+ @Override
+ public void handle(PacketFDEncryptionRequest encryptRequest) throws Exception
+ {
+ Preconditions.checkState( thisState == State.ENCRYPT_REQUEST, "Not expecting ENCRYPT_REQUEST" );
+
+ // Only need to handle this if we want to use encryption
+ if ( user.getPendingConnection().getForgeLogin() != null )
+ {
+ PublicKey publickey = EncryptionUtil.getPubkey( encryptRequest );
+ this.secretkey = EncryptionUtil.getSecret();
+
+ byte[] shared = EncryptionUtil.encrypt( publickey, secretkey.getEncoded() );
+ byte[] token = EncryptionUtil.encrypt( publickey, encryptRequest.getVerifyToken() );
+
+ ch.write( new PacketFCEncryptionResponse( shared, token ) );
+
+ Cipher encrypt = EncryptionUtil.getCipher( Cipher.ENCRYPT_MODE, secretkey );
+ ch.addBefore( PipelineUtils.PACKET_DECODE_HANDLER, PipelineUtils.ENCRYPT_HANDLER, new CipherEncoder( encrypt ) );
+
+ thisState = State.ENCRYPT_RESPONSE;
+ } else
+ {
+ thisState = State.LOGIN;
+ }
+ }
+
+ @Override
+ public void handle(PacketFCEncryptionResponse encryptResponse) throws Exception
+ {
+ Preconditions.checkState( thisState == State.ENCRYPT_RESPONSE, "Not expecting ENCRYPT_RESPONSE" );
+
+ Cipher decrypt = EncryptionUtil.getCipher( Cipher.DECRYPT_MODE, secretkey );
+ ch.addBefore( PipelineUtils.PACKET_DECODE_HANDLER, PipelineUtils.DECRYPT_HANDLER, new CipherDecoder( decrypt ) );
+
+ ch.write( user.getPendingConnection().getForgeLogin() );
+
+ ch.write( PacketConstants.CLIENT_LOGIN );
+ thisState = State.LOGIN;
+ }
+
+ @Override
+ public void handle(PacketFFKick kick) throws Exception
+ {
+ ServerInfo def = bungee.getServerInfo( user.getPendingConnection().getListener().getFallbackServer() );
+ if ( Objects.equals( target, def ) )
+ {
+ def = null;
+ }
+ ServerKickEvent event = bungee.getPluginManager().callEvent( new ServerKickEvent( user, kick.getMessage(), def, ServerKickEvent.State.CONNECTING ) );
+ if ( event.isCancelled() && event.getCancelServer() != null )
+ {
+ user.connect( event.getCancelServer() );
+ return;
+ }
+
+ String message = bungee.getTranslation( "connect_kick" ) + target.getName() + ": " + event.getKickReason();
+ if ( user.getServer() == null )
+ {
+ user.disconnect( message );
+ } else
+ {
+ user.sendMessage( message );
+ }
+ }
+
+ @Override
+ public void handle(final PacketFAPluginMessage pluginMessage) throws Exception {
+ if (pluginMessage.equals(PacketConstants.I_AM_BUNGEE) && !BungeeCord.getInstance().config.allowBungeeOnBungee()) {
+ throw new IllegalStateException("May not connect to another BungeeCord!");
+ }
+ if (pluginMessage.getTag().equals("FML") && (pluginMessage.getData()[0] & 0xFF) == 0x0) {
+ final ByteArrayDataInput in = ByteStreams.newDataInput(pluginMessage.getData());
+ in.readUnsignedByte();
+ for (int count = in.readInt(), i = 0; i < count; ++i) {
+ in.readUTF();
+ }
+ if (in.readByte() != 0) {
+ ((PacketDecoder)this.ch.getHandle().pipeline().get((Class)PacketDecoder.class)).setProtocol(Forge.getInstance());
+ }
+ }
+ this.user.unsafe().sendPacket(pluginMessage);
+ if (!this.sentMessages && this.user.getPendingConnection().getForgeLogin() != null) {
+ for (final PacketFAPluginMessage message : this.user.getPendingConnection().getLoginMessages()) {
+ this.ch.write(message);
+ }
+ this.sentMessages = true;
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return "[" + user.getName() + "] <-> ServerConnector [" + target.getName() + "]";
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/UserConnection.java b/eaglerbungee/src/main/java/net/md_5/bungee/UserConnection.java
new file mode 100644
index 0000000..cb96059
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/UserConnection.java
@@ -0,0 +1,447 @@
+package net.md_5.bungee;
+
+import com.google.common.base.Preconditions;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.*;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.util.concurrent.GenericFutureListener;
+import io.netty.util.internal.PlatformDependent;
+
+import java.beans.ConstructorProperties;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.logging.Level;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.config.ServerInfo;
+import net.md_5.bungee.api.connection.Connection;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.event.PermissionCheckEvent;
+import net.md_5.bungee.api.event.ServerConnectEvent;
+import net.md_5.bungee.api.score.Scoreboard;
+import net.md_5.bungee.api.tab.TabListHandler;
+import net.md_5.bungee.connection.InitialHandler;
+import net.md_5.bungee.eaglercraft.RedirectServerInfo;
+import net.md_5.bungee.netty.ChannelWrapper;
+import net.md_5.bungee.netty.HandlerBoss;
+import net.md_5.bungee.netty.PacketWrapper;
+import net.md_5.bungee.netty.PipelineUtils;
+import net.md_5.bungee.protocol.packet.DefinedPacket;
+import net.md_5.bungee.protocol.packet.Packet3Chat;
+import net.md_5.bungee.protocol.packet.PacketCCSettings;
+import net.md_5.bungee.protocol.packet.PacketFAPluginMessage;
+import net.md_5.bungee.protocol.packet.PacketFFKick;
+import net.md_5.bungee.util.CaseInsensitiveSet;
+
+public final class UserConnection implements ProxiedPlayer
+{
+
+ /*========================================================================*/
+ private final ProxyServer bungee;
+ private final ChannelWrapper ch;
+ private final String name;
+ private final InitialHandler pendingConnection;
+ /*========================================================================*/
+ private ServerConnection server;
+ private Object switchMutex = new Object();
+ private Collection pendingConnects = new HashSet<>();
+ /*========================================================================*/
+ private TabListHandler tabList;
+ private int sentPingId;
+
+ private long sentPingTime;
+ private int ping = 100;
+ private ServerInfo reconnectServer;
+ /*========================================================================*/
+ private Collection groups = new CaseInsensitiveSet();
+ private Collection permissions = new CaseInsensitiveSet();
+ /*========================================================================*/
+
+
+ private int serverEntityId;
+ private PacketCCSettings settings;
+
+ private Scoreboard serverSentScoreboard = new Scoreboard();
+ /*========================================================================*/
+
+ private String displayName;
+ /*========================================================================*/
+
+ private int clientEntityId;
+
+ public void setServer(ServerConnection server) {
+ this.server = server;
+ }
+
+ public void setSettings(PacketCCSettings settings){
+ this.settings = settings;
+ }
+
+ public void setServerEntityId(int serverEntityId) {
+ this.serverEntityId = serverEntityId;
+ }
+
+ public void setSentPingId(int sentPingId) {
+ this.sentPingId = sentPingId;
+ }
+
+ public void setSentPingTime(long sentPingTime) {
+ this.sentPingTime = sentPingTime;
+ }
+
+ public void setPing(int ping) {
+ this.ping = ping;
+ }
+
+ @Override
+ public void setReconnectServer(ServerInfo reconnectServer) {
+ this.reconnectServer = reconnectServer;
+ }
+
+ public void setClientEntityId(int clientEntityId) {
+ this.clientEntityId = clientEntityId;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public InitialHandler getPendingConnection() {
+ return pendingConnection;
+ }
+
+ @Override
+ public TabListHandler getTabList() {
+ return tabList;
+ }
+
+ public Collection getPendingConnects() {
+ return pendingConnects;
+ }
+
+ public Object getSwitchMutex() {
+ return switchMutex;
+ }
+
+ @Override
+ public ServerConnection getServer() {
+ return server;
+ }
+
+ public int getSentPingId() {
+ return sentPingId;
+ }
+
+ public long getSentPingTime() {
+ return sentPingTime;
+ }
+
+ @Override
+ public int getPing() {
+ return ping;
+ }
+
+ @Override
+ public ServerInfo getReconnectServer() {
+ return reconnectServer;
+ }
+
+ public Collection getPermissions() {
+ return permissions;
+ }
+
+ public int getClientEntityId() {
+ return clientEntityId;
+ }
+
+ public int getServerEntityId() {
+ return serverEntityId;
+ }
+
+ public PacketCCSettings getSettings() {
+ return settings;
+ }
+
+ public Scoreboard getServerSentScoreboard() {
+ return serverSentScoreboard;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ private Unsafe unsafe = new Unsafe()
+ {
+ @Override
+ public void sendPacket(DefinedPacket packet)
+ {
+ ch.write( packet );
+ }
+ };
+
+ @ConstructorProperties({ "bungee", "ch", "name", "pendingConnection" })
+ public UserConnection(final ProxyServer bungee, final ChannelWrapper ch, final String name, final InitialHandler pendingConnection) {
+ this.switchMutex = new Object();
+ this.pendingConnects = new HashSet();
+ this.ping = 100;
+ this.groups = (Collection) new CaseInsensitiveSet();
+ this.permissions = (Collection) new CaseInsensitiveSet();
+ this.serverSentScoreboard = new Scoreboard();
+ this.unsafe = new Connection.Unsafe() {
+ @Override
+ public void sendPacket(final DefinedPacket packet) {
+ UserConnection.this.ch.write(packet);
+ }
+ };
+ if (bungee == null) {
+ throw new NullPointerException("bungee");
+ }
+ if (ch == null) {
+ throw new NullPointerException("ch");
+ }
+ if (name == null) {
+ throw new NullPointerException("name");
+ }
+ this.bungee = bungee;
+ this.ch = ch;
+ this.name = name;
+ this.pendingConnection = pendingConnection;
+ }
+
+ public void init()
+ {
+ this.displayName = name;
+ try
+ {
+ this.tabList = getPendingConnection().getListener().getTabList().getDeclaredConstructor().newInstance();
+ } catch ( ReflectiveOperationException ex )
+ {
+ throw new RuntimeException( ex );
+ }
+ this.tabList.init( this );
+
+ Collection g = bungee.getConfigurationAdapter().getGroups( name );
+ for ( String s : g )
+ {
+ addGroups( s );
+ }
+ }
+
+ @Override
+ public void setTabList(TabListHandler tabList)
+ {
+ tabList.init( this );
+ this.tabList = tabList;
+ }
+
+ public void sendPacket(PacketWrapper packet)
+ {
+ ch.write( packet );
+ }
+
+ @Deprecated
+ public boolean isActive()
+ {
+ return !ch.isClosed();
+ }
+
+ @Override
+ public void setDisplayName(String name)
+ {
+ Preconditions.checkNotNull( name, "displayName" );
+ Preconditions.checkArgument( name.length() <= 16, "Display name cannot be longer than 16 characters" );
+ getTabList().onDisconnect();
+ displayName = name;
+ getTabList().onConnect();
+ }
+
+ @Override
+ public void connect(ServerInfo target)
+ {
+ connect( target, false );
+ }
+
+ void sendDimensionSwitch()
+ {
+ unsafe().sendPacket( PacketConstants.DIM1_SWITCH );
+ unsafe().sendPacket( PacketConstants.DIM2_SWITCH );
+ }
+
+ public void connectNow(ServerInfo target)
+ {
+ sendDimensionSwitch();
+ connect( target );
+ }
+
+ public void connect(final ServerInfo info, final boolean retry) {
+ if(info instanceof RedirectServerInfo) {
+ sendData("EAG|Reconnect", ((RedirectServerInfo)info).getRedirect().getBytes(StandardCharsets.UTF_8));
+ return;
+ }
+ final ServerConnectEvent event = new ServerConnectEvent(this, info);
+ if (this.bungee.getPluginManager().callEvent(event).isCancelled()) {
+ return;
+ }
+ final BungeeServerInfo target = (BungeeServerInfo) event.getTarget();
+ if (this.getServer() != null && this.getServer().getInfo() != null && Objects.equals(this.getServer().getInfo(), target)) {
+ //this.sendMessage(ChatColor.RED + "Cannot connect to server you are already on!");
+ return;
+ }
+ if (this.pendingConnects.contains(target)) {
+ this.sendMessage(ChatColor.RED + "Already connecting to this server!");
+ return;
+ }
+ this.pendingConnects.add(target);
+ final ChannelInitializer initializer = new ChannelInitializer() {
+ protected void initChannel(final Channel ch) throws Exception {
+ PipelineUtils.BASE.initChannel(ch);
+ ((HandlerBoss) ch.pipeline().get((Class) HandlerBoss.class)).setHandler(new ServerConnector(UserConnection.this.bungee, UserConnection.this, target));
+ }
+ };
+ final ChannelFutureListener listener = (ChannelFutureListener) new ChannelFutureListener() {
+ public void operationComplete(final ChannelFuture future) throws Exception {
+ if (!future.isSuccess()) {
+ future.channel().close();
+ UserConnection.this.pendingConnects.remove(target);
+ final ServerInfo def = ProxyServer.getInstance().getServers().get(UserConnection.this.getPendingConnection().getListener().getFallbackServer());
+ if ((retry & target != def) && ((UserConnection.this.getServer() == null || UserConnection.this.getServer().getInfo() == null) || def != UserConnection.this.getServer().getInfo())) {
+ UserConnection.this.sendMessage(UserConnection.this.bungee.getTranslation("fallback_lobby"));
+ UserConnection.this.connect(def, false);
+ } else if (UserConnection.this.getServer() == null || UserConnection.this.getServer().getInfo() == null) {
+ UserConnection.this.disconnect(UserConnection.this.bungee.getTranslation("fallback_kick") + future.cause().getClass().getName());
+ } else {
+ UserConnection.this.sendMessage(UserConnection.this.bungee.getTranslation("fallback_kick") + future.cause().getClass().getName());
+ }
+ }
+ }
+ };
+ final Bootstrap b = ((Bootstrap) ((Bootstrap) ((Bootstrap) ((Bootstrap) new Bootstrap().channel((Class) NioSocketChannel.class)).group((EventLoopGroup) BungeeCord.getInstance().eventLoops)).handler((ChannelHandler) initializer))
+ .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, Integer.valueOf(5000))).remoteAddress((SocketAddress) target.getAddress());
+ if (!PlatformDependent.isWindows()) {
+ b.localAddress(this.getPendingConnection().getListener().getHost().getHostString(), 0);
+ }
+ b.connect().addListener((GenericFutureListener) listener);
+ }
+
+ @Override
+ public synchronized void disconnect(String reason)
+ {
+ if ( ch.getHandle().isActive() )
+ {
+ bungee.getLogger().log( Level.INFO, "[" + getName() + "] disconnected with: " + reason );
+ unsafe().sendPacket( new PacketFFKick( reason ) );
+ ch.close();
+ if ( server != null )
+ {
+ server.disconnect( "Quitting" );
+ }
+ }
+ }
+
+ @Override
+ public void chat(String message)
+ {
+ Preconditions.checkState( server != null, "Not connected to server" );
+ server.getCh().write( new Packet3Chat( message ) );
+ }
+
+ @Override
+ public void sendMessage(String message)
+ {
+ this.unsafe().sendPacket(new Packet3Chat(message));
+ }
+
+ @Override
+ public void sendMessages(String... messages)
+ {
+ for ( String message : messages )
+ {
+ sendMessage( message );
+ }
+ }
+
+ @Override
+ public void sendData(String channel, byte[] data)
+ {
+ unsafe().sendPacket( new PacketFAPluginMessage( channel, data ) );
+ }
+
+ @Override
+ public InetSocketAddress getAddress()
+ {
+ return (InetSocketAddress) ch.getHandle().remoteAddress();
+ }
+
+ @Override
+ public Collection getGroups()
+ {
+ return Collections.unmodifiableCollection( groups );
+ }
+
+ @Override
+ public void addGroups(String... groups)
+ {
+ for ( String group : groups )
+ {
+ this.groups.add( group );
+ for ( String permission : bungee.getConfigurationAdapter().getPermissions( group ) )
+ {
+ setPermission( permission, true );
+ }
+ }
+ }
+
+ @Override
+ public void removeGroups(String... groups)
+ {
+ for ( String group : groups )
+ {
+ this.groups.remove( group );
+ for ( String permission : bungee.getConfigurationAdapter().getPermissions( group ) )
+ {
+ setPermission( permission, false );
+ }
+ }
+ }
+
+ @Override
+ public boolean hasPermission(String permission)
+ {
+ return bungee.getPluginManager().callEvent( new PermissionCheckEvent( this, permission, permissions.contains( permission ) ) ).hasPermission();
+ }
+
+ @Override
+ public void setPermission(String permission, boolean value)
+ {
+ if ( value )
+ {
+ permissions.add( permission );
+ } else
+ {
+ permissions.remove( permission );
+ }
+ }
+
+ @Override
+ public Map getAttachment() { // fix this (maybe)
+ System.out.println("This might be a problem");
+ return Map.of();
+ }
+
+ @Override
+ public String toString()
+ {
+ return name;
+ }
+
+ @Override
+ public Unsafe unsafe()
+ {
+ return unsafe;
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/Util.java b/eaglerbungee/src/main/java/net/md_5/bungee/Util.java
new file mode 100644
index 0000000..b42808f
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/Util.java
@@ -0,0 +1,73 @@
+package net.md_5.bungee;
+
+import java.net.InetSocketAddress;
+import java.util.Collection;
+
+/**
+ * Series of utility classes to perform various operations.
+ */
+public class Util
+{
+
+ private static final int DEFAULT_PORT = 25565;
+
+ /**
+ * Method to transform human readable addresses into usable address objects.
+ *
+ * @param hostline in the format of 'host:port'
+ * @return the constructed hostname + port.
+ */
+ public static InetSocketAddress getAddr(String hostline)
+ {
+ String[] split = hostline.split( ":" );
+ int port = DEFAULT_PORT;
+ if ( split.length > 1 )
+ {
+ port = Integer.parseInt( split[1] );
+ }
+ return new InetSocketAddress( split[0], port );
+ }
+
+ /**
+ * Formats an integer as a hex value.
+ *
+ * @param i the integer to format
+ * @return the hex representation of the integer
+ */
+ public static String hex(int i)
+ {
+ return String.format( "0x%02X", i );
+ }
+
+ /**
+ * Constructs a pretty one line version of a {@link Throwable}. Useful for
+ * debugging.
+ *
+ * @param t the {@link Throwable} to format.
+ * @return a string representing information about the {@link Throwable}
+ */
+ public static String exception(Throwable t)
+ {
+ // TODO: We should use clear manually written exceptions
+ StackTraceElement[] trace = t.getStackTrace();
+ return t.getClass().getSimpleName() + " : " + t.getMessage()
+ + ( ( trace.length > 0 ) ? " @ " + t.getStackTrace()[0].getClassName() + ":" + t.getStackTrace()[0].getLineNumber() : "" );
+ }
+
+ public static String csv(Iterable> objects)
+ {
+ return format( objects, ", " );
+ }
+
+ public static String format(Iterable> objects, String separators)
+ {
+ StringBuilder ret = new StringBuilder();
+ for ( Object o : objects )
+ {
+ ret.append( o );
+ ret.append( separators );
+ }
+
+ return ( ret.length() == 0 ) ? "" : ret.substring( 0, ret.length() - separators.length() );
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/AbstractReconnectHandler.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/AbstractReconnectHandler.java
new file mode 100644
index 0000000..2adcd57
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/AbstractReconnectHandler.java
@@ -0,0 +1,46 @@
+package net.md_5.bungee.api;
+
+import com.google.common.base.Preconditions;
+import net.md_5.bungee.api.config.ServerInfo;
+import net.md_5.bungee.api.connection.PendingConnection;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+
+public abstract class AbstractReconnectHandler implements ReconnectHandler
+{
+
+ @Override
+ public ServerInfo getServer(ProxiedPlayer player)
+ {
+ ServerInfo server = getForcedHost( player.getPendingConnection() );
+ if ( server == null )
+ {
+ server = getStoredServer( player );
+ if ( server == null )
+ {
+ server = ProxyServer.getInstance().getServerInfo( player.getPendingConnection().getListener().getDefaultServer() );
+ }
+
+ Preconditions.checkState( server != null, "Default server not defined" );
+ }
+
+ return server;
+ }
+
+ public static ServerInfo getForcedHost(PendingConnection con)
+ {
+ if ( con.getVirtualHost() == null )
+ {
+ return null;
+ }
+
+ String forced = con.getListener().getForcedHosts().get( con.getVirtualHost().getHostString() );
+
+ if ( forced == null && con.getListener().isForceDefault() )
+ {
+ forced = con.getListener().getDefaultServer();
+ }
+ return ProxyServer.getInstance().getServerInfo( forced );
+ }
+
+ protected abstract ServerInfo getStoredServer(ProxiedPlayer player);
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/Callback.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/Callback.java
new file mode 100644
index 0000000..0cccc17
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/Callback.java
@@ -0,0 +1,19 @@
+package net.md_5.bungee.api;
+
+/**
+ * Represents a method which may be called once a result has been computed
+ * asynchronously.
+ *
+ * @param the type of result
+ */
+public interface Callback
+{
+
+ /**
+ * Called when the result is done.
+ *
+ * @param result the result of the computation
+ * @param error the error(s) that occurred, if any
+ */
+ public void done(V result, Throwable error);
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/ChatColor.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/ChatColor.java
new file mode 100644
index 0000000..0df1cd5
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/ChatColor.java
@@ -0,0 +1,186 @@
+package net.md_5.bungee.api;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * Simplistic enumeration of all supported color values for chat.
+ */
+public enum ChatColor
+{
+
+ /**
+ * Represents black.
+ */
+ BLACK( '0' ),
+ /**
+ * Represents dark blue.
+ */
+ DARK_BLUE( '1' ),
+ /**
+ * Represents dark green.
+ */
+ DARK_GREEN( '2' ),
+ /**
+ * Represents dark blue (aqua).
+ */
+ DARK_AQUA( '3' ),
+ /**
+ * Represents dark red.
+ */
+ DARK_RED( '4' ),
+ /**
+ * Represents dark purple.
+ */
+ DARK_PURPLE( '5' ),
+ /**
+ * Represents gold.
+ */
+ GOLD( '6' ),
+ /**
+ * Represents gray.
+ */
+ GRAY( '7' ),
+ /**
+ * Represents dark gray.
+ */
+ DARK_GRAY( '8' ),
+ /**
+ * Represents blue.
+ */
+ BLUE( '9' ),
+ /**
+ * Represents green.
+ */
+ GREEN( 'a' ),
+ /**
+ * Represents aqua.
+ */
+ AQUA( 'b' ),
+ /**
+ * Represents red.
+ */
+ RED( 'c' ),
+ /**
+ * Represents light purple.
+ */
+ LIGHT_PURPLE( 'd' ),
+ /**
+ * Represents yellow.
+ */
+ YELLOW( 'e' ),
+ /**
+ * Represents white.
+ */
+ WHITE( 'f' ),
+ /**
+ * Represents magical characters that change around randomly.
+ */
+ MAGIC( 'k' ),
+ /**
+ * Makes the text bold.
+ */
+ BOLD( 'l' ),
+ /**
+ * Makes a line appear through the text.
+ */
+ STRIKETHROUGH( 'm' ),
+ /**
+ * Makes the text appear underlined.
+ */
+ UNDERLINE( 'n' ),
+ /**
+ * Makes the text italic.
+ */
+ ITALIC( 'o' ),
+ /**
+ * Resets all previous chat colors or formats.
+ */
+ RESET( 'r' );
+ /**
+ * The special character which prefixes all chat colour codes. Use this if
+ * you need to dynamically convert colour codes from your custom format.
+ */
+ public static final char COLOR_CHAR = '\u00A7';
+ /**
+ * Pattern to remove all colour codes.
+ */
+ private static final Pattern STRIP_COLOR_PATTERN = Pattern.compile( "(?i)" + String.valueOf( COLOR_CHAR ) + "[0-9A-FK-OR]" );
+ /**
+ * Colour instances keyed by their active character.
+ */
+ private static final Map BY_CHAR = new HashMap<>();
+ /**
+ * The code appended to {@link #COLOR_CHAR} to make usable colour.
+ */
+ private final char code;
+ /**
+ * This colour's colour char prefixed by the {@link #COLOR_CHAR}.
+ */
+ private final String toString;
+
+ static
+ {
+ for ( ChatColor colour : values() )
+ {
+ BY_CHAR.put( colour.code, colour );
+ }
+ }
+
+ private ChatColor(char code)
+ {
+ this.code = code;
+ this.toString = new String( new char[]
+ {
+ COLOR_CHAR, code
+ } );
+ }
+
+ @Override
+ public String toString()
+ {
+ return toString;
+ }
+
+ /**
+ * Strips the given message of all color codes
+ *
+ * @param input String to strip of color
+ * @return A copy of the input string, without any coloring
+ */
+ public static String stripColor(final String input)
+ {
+ if ( input == null )
+ {
+ return null;
+ }
+
+ return STRIP_COLOR_PATTERN.matcher( input ).replaceAll( "" );
+ }
+
+ public static String translateAlternateColorCodes(char altColorChar, String textToTranslate)
+ {
+ char[] b = textToTranslate.toCharArray();
+ for ( int i = 0; i < b.length - 1; i++ )
+ {
+ if ( b[i] == altColorChar && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf( b[i + 1] ) > -1 )
+ {
+ b[i] = ChatColor.COLOR_CHAR;
+ b[i + 1] = Character.toLowerCase( b[i + 1] );
+ }
+ }
+ return new String( b );
+ }
+
+ /**
+ * Get the colour represented by the specified code.
+ *
+ * @param code the code to search for
+ * @return the mapped colour, or null if non exists
+ */
+ public static ChatColor getByChar(char code)
+ {
+ return BY_CHAR.get( code );
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/CommandSender.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/CommandSender.java
new file mode 100644
index 0000000..5bd40e3
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/CommandSender.java
@@ -0,0 +1,70 @@
+package net.md_5.bungee.api;
+
+import java.util.Collection;
+import java.util.Map;
+
+public interface 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
+ */
+ public void sendMessage(String message);
+
+ /**
+ * Send several messages to this sender. Each message will be sent
+ * separately.
+ *
+ * @param messages the messages to send
+ */
+ public void sendMessages(String... messages);
+
+ /**
+ * Get all groups this user is part of. This returns an unmodifiable
+ * collection.
+ *
+ * @return the users groups
+ */
+ public Collection getGroups();
+
+ /**
+ * Adds groups to a this user for the current session only.
+ *
+ * @param groups the groups to add
+ */
+ public void addGroups(String... groups);
+
+ /**
+ * Remove groups from this user for the current session only.
+ *
+ * @param groups the groups to remove
+ */
+ 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.
+ *
+ * @param permission the node to set
+ * @param value the value of the node
+ */
+ public void setPermission(String permission, boolean value);
+
+ Map getAttachment();
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/MOTD.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/MOTD.java
new file mode 100644
index 0000000..8608aa6
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/MOTD.java
@@ -0,0 +1,25 @@
+package net.md_5.bungee.api;
+
+import java.util.List;
+
+public interface MOTD extends QueryConnection {
+
+ public void sendToUser();
+
+ public String getLine1();
+ public String getLine2();
+ public List getPlayerList();
+ public int[] getBitmap();
+ public int getOnlinePlayers();
+ public int getMaxPlayers();
+ public String getSubType();
+
+ public void setLine1(String p);
+ public void setLine2(String p);
+ public void setPlayerList(List p);
+ public void setPlayerList(String... p);
+ public void setBitmap(int[] p);
+ public void setOnlinePlayers(int i);
+ public void setMaxPlayers(int i);
+
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/ProxyServer.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/ProxyServer.java
new file mode 100644
index 0000000..a91ead3
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/ProxyServer.java
@@ -0,0 +1,251 @@
+package net.md_5.bungee.api;
+
+import net.md_5.bungee.api.plugin.PluginManager;
+import com.google.common.base.Preconditions;
+import java.io.File;
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.Map;
+import java.util.logging.Logger;
+import net.md_5.bungee.api.config.ConfigurationAdapter;
+import net.md_5.bungee.api.config.ServerInfo;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.plugin.Plugin;
+import net.md_5.bungee.api.scheduler.TaskScheduler;
+import net.md_5.bungee.api.tab.CustomTabList;
+
+public abstract class ProxyServer
+{
+
+ private static ProxyServer instance;
+
+ public static ProxyServer getInstance(){
+ return instance;
+ }
+
+ /**
+ * Sets the proxy instance. This method may only be called once per an
+ * application.
+ *
+ * @param instance the new instance to set
+ */
+ public static void setInstance(ProxyServer instance)
+ {
+ Preconditions.checkNotNull( instance, "instance" );
+ Preconditions.checkArgument( ProxyServer.instance == null, "Instance already set" );
+ ProxyServer.instance = instance;
+ }
+
+ /**
+ * Gets the name of the currently running proxy software.
+ *
+ * @return the name of this instance
+ */
+ public abstract String getName();
+
+ /**
+ * Gets the version of the currently running proxy software.
+ *
+ * @return the version of this instance
+ */
+ public abstract String getVersion();
+
+ /**
+ * Gets a localized string from the .properties file.
+ *
+ * @return the localized string
+ */
+ public abstract String getTranslation(String name, Object... args);
+
+ /**
+ * Gets the main logger which can be used as a suitable replacement for
+ * {@link System#out} and {@link System#err}.
+ *
+ * @return the {@link Logger} instance
+ */
+ public abstract Logger getLogger();
+
+ /**
+ * Return all players currently connected.
+ *
+ * @return all connected players
+ */
+ public abstract Collection getPlayers();
+
+ /**
+ * Gets a connected player via their unique username.
+ *
+ * @param name of the player
+ * @return their player instance
+ */
+ public abstract ProxiedPlayer getPlayer(String name);
+
+ /**
+ * 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
+ */
+ public abstract Map getServers();
+
+ /**
+ * Gets the server info of a server.
+ *
+ * @param name the name of the configured server
+ * @return the server info belonging to the specified server
+ */
+ public abstract ServerInfo getServerInfo(String name);
+
+ /**
+ * Get the {@link PluginManager} associated with loading plugins and
+ * dispatching events. It is recommended that implementations use the
+ * provided PluginManager class.
+ *
+ * @return the plugin manager
+ */
+ public abstract PluginManager getPluginManager();
+
+ /**
+ * Returns the currently in use configuration adapter.
+ *
+ * @return the used configuration adapter
+ */
+ public abstract ConfigurationAdapter getConfigurationAdapter();
+
+ /**
+ * Set the configuration adapter to be used. Must be called from
+ * {@link Plugin#onLoad()}.
+ *
+ * @param adapter the adapter to use
+ */
+ public abstract void setConfigurationAdapter(ConfigurationAdapter adapter);
+
+ /**
+ * Get the currently in use reconnect handler.
+ *
+ * @return the in use reconnect handler
+ */
+ public abstract ReconnectHandler getReconnectHandler();
+
+ /**
+ * Sets the reconnect handler to be used for subsequent connections.
+ *
+ * @param handler the new handler
+ */
+ public abstract void setReconnectHandler(ReconnectHandler handler);
+
+ /**
+ * Gracefully mark this instance for shutdown.
+ */
+ public abstract void stop();
+
+ /**
+ * Start this instance so that it may accept connections.
+ *
+ * @throws Exception any exception thrown during startup causing the
+ * instance to fail to boot
+ */
+ public abstract void start() throws Exception;
+
+ /**
+ * Register a channel for use with plugin messages. This is required by some
+ * server / client implementations.
+ *
+ * @param channel the channel to register
+ */
+ public abstract void registerChannel(String channel);
+
+ /**
+ * Unregister a previously registered channel.
+ *
+ * @param channel the channel to unregister
+ */
+ public abstract void unregisterChannel(String channel);
+
+ /**
+ * Get an immutable set of all registered plugin channels.
+ *
+ * @return registered plugin channels
+ */
+ public abstract Collection getChannels();
+
+ /**
+ * Get the Minecraft version supported by this proxy.
+ *
+ * @return the supported Minecraft version
+ */
+ public abstract String getGameVersion();
+
+ /**
+ * Get the Minecraft protocol version supported by this proxy.
+ *
+ * @return the Minecraft protocol version
+ */
+ public abstract byte getProtocolVersion();
+
+ /**
+ * Factory method to construct an implementation specific server info
+ * instance.
+ *
+ * @param name name of the server
+ * @param address connectable Minecraft address + port of the server
+ * @param restricted whether the server info restricted property will be set
+ * @return the constructed instance
+ */
+ public abstract ServerInfo constructServerInfo(String name, InetSocketAddress address, boolean restricted);
+
+ /**
+ * Returns the console overlord for this proxy. Being the console, this
+ * command server cannot have permissions or groups, and will be able to
+ * execute anything.
+ *
+ * @return the console command sender of this proxy
+ */
+ public abstract CommandSender getConsole();
+
+ /**
+ * Return the folder used to load plugins from.
+ *
+ * @return the folder used to load plugin
+ */
+ public abstract File getPluginsFolder();
+
+ /**
+ * Get the scheduler instance for this proxy.
+ *
+ * @return the in use scheduler
+ */
+ public abstract TaskScheduler getScheduler();
+
+ /**
+ * Get the current number of connected users. The default implementation is
+ * more efficient than {@link #getPlayers()} as it does not take a lock or
+ * make a copy.
+ *
+ * @return the current number of connected players
+ */
+ public abstract int getOnlineCount();
+
+ /**
+ * Send the specified message to the console and all connected players.
+ *
+ * @param message the message to broadcast
+ */
+ public abstract void broadcast(String message);
+
+ /**
+ * Gets a new instance of this proxies custom tab list.
+ *
+ * @param player the player to generate this list in the context of
+ * @return a new {@link CustomTabList} instance
+ */
+ public abstract CustomTabList customTabList(ProxiedPlayer player);
+
+ /**
+ * Gets the commands which are disabled and will not be run on this proxy.
+ *
+ * @return the set of disabled commands
+ */
+ public abstract Collection getDisabledCommands();
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/QueryConnection.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/QueryConnection.java
new file mode 100644
index 0000000..dbca191
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/QueryConnection.java
@@ -0,0 +1,70 @@
+package net.md_5.bungee.api;
+
+import java.net.InetAddress;
+
+import org.json.JSONObject;
+
+import net.md_5.bungee.BungeeCord;
+import net.md_5.bungee.api.config.ListenerInfo;
+import net.md_5.bungee.eaglercraft.EaglercraftBungee;
+
+public interface QueryConnection {
+
+ public InetAddress getRemoteAddress();
+ public ListenerInfo getListener();
+
+ public String getAccept();
+ public void setReturnType(String type);
+ public String getReturnType();
+
+ public int availableRequests();
+
+ public default JSONObject readRequestData() {
+ String s = readRequestString();
+ return s == null ? null : new JSONObject(s);
+ }
+
+ public String readRequestString();
+ public long getConnectionTimestamp();
+
+ public default long getConnectionAge() {
+ return System.currentTimeMillis() - getConnectionTimestamp();
+ }
+
+ public default void writeResponse(JSONObject msg) {
+ JSONObject toSend = new JSONObject();
+ toSend.put("type", getReturnType());
+ toSend.put("name", BungeeCord.getInstance().config.getServerName());
+ toSend.put("brand", EaglercraftBungee.brand);
+ toSend.put("vers", EaglercraftBungee.version);
+ toSend.put("cracked", EaglercraftBungee.cracked);
+ toSend.put("secure", false);
+ toSend.put("time", System.currentTimeMillis());
+ toSend.put("uuid", BungeeCord.getInstance().config.getUuid());
+ toSend.put("data", msg);
+ writeResponseRaw(toSend.toString());
+ }
+
+ public default void writeResponse(String msg) {
+ JSONObject toSend = new JSONObject();
+ toSend.put("type", getReturnType());
+ toSend.put("name", BungeeCord.getInstance().config.getServerName());
+ toSend.put("brand", EaglercraftBungee.brand);
+ toSend.put("vers", EaglercraftBungee.version);
+ toSend.put("cracked", EaglercraftBungee.cracked);
+ toSend.put("secure", false);
+ toSend.put("time", System.currentTimeMillis());
+ toSend.put("uuid", BungeeCord.getInstance().config.getUuid());
+ toSend.put("data", msg);
+ writeResponseRaw(toSend.toString());
+ }
+
+ public void writeResponseRaw(String msg);
+ public void writeResponseBinary(byte[] blob);
+
+ public void keepAlive(boolean yes);
+ public boolean shouldKeepAlive();
+ public boolean isClosed();
+ public void close();
+
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/ReconnectHandler.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/ReconnectHandler.java
new file mode 100644
index 0000000..8b1b48a
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/ReconnectHandler.java
@@ -0,0 +1,39 @@
+package net.md_5.bungee.api;
+
+import net.md_5.bungee.api.config.ServerInfo;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+
+public interface ReconnectHandler
+{
+
+ /**
+ * Gets the initial server name for a connecting player.
+ *
+ * @param player the connecting player
+ * @return the server to connect to
+ */
+ ServerInfo getServer(ProxiedPlayer player);
+
+ /**
+ * Save the server of this player before they disconnect so it can be
+ * retrieved later.
+ *
+ * @param player the player to save
+ */
+ void setServer(ProxiedPlayer player); // TOOD: String + String arguments?
+
+ /**
+ * Save all pending reconnect locations. Whilst not used for database
+ * connections, this method will be called at a predefined interval to allow
+ * the saving of reconnect files.
+ */
+ void save();
+
+ /**
+ * Close all connections indicating that the proxy is about to shutdown and
+ * all data should be saved. No new requests will be made after this method
+ * has been called.
+ *
+ */
+ void close();
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/ServerIcon.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/ServerIcon.java
new file mode 100644
index 0000000..ea4d8b1
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/ServerIcon.java
@@ -0,0 +1,66 @@
+package net.md_5.bungee.api;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.InputStream;
+
+import javax.imageio.ImageIO;
+
+public class ServerIcon {
+
+ public static int[] createServerIcon(BufferedImage awtIcon) {
+ BufferedImage icon = awtIcon;
+ boolean gotScaled = false;
+ if(icon.getWidth() != 64 || icon.getHeight() != 64) {
+ icon = new BufferedImage(64, 64, awtIcon.getType());
+ Graphics2D g = (Graphics2D) icon.getGraphics();
+ g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, (awtIcon.getWidth() < 64 || awtIcon.getHeight() < 64) ?
+ RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR : RenderingHints.VALUE_INTERPOLATION_BICUBIC);
+ g.setBackground(new Color(0, true));
+ g.clearRect(0, 0, 64, 64);
+ int ow = awtIcon.getWidth();
+ int oh = awtIcon.getHeight();
+ int nw, nh;
+ float aspectRatio = (float)oh / (float)ow;
+ if(aspectRatio >= 1.0f) {
+ nw = (int)(64 / aspectRatio);
+ nh = 64;
+ }else {
+ nw = 64;
+ nh = (int)(64 * aspectRatio);
+ }
+ g.drawImage(awtIcon, (64 - nw) / 2, (64 - nh) / 2, (64 - nw) / 2 + nw, (64 - nh) / 2 + nh, 0, 0, awtIcon.getWidth(), awtIcon.getHeight(), null);
+ g.dispose();
+ gotScaled = true;
+ }
+ int[] pxls = icon.getRGB(0, 0, 64, 64, new int[4096], 0, 64);
+ if(gotScaled) {
+ for(int i = 0; i < pxls.length; ++i) {
+ if((pxls[i] & 0xFFFFFF) == 0) {
+ pxls[i] = 0;
+ }
+ }
+ }
+ return pxls;
+ }
+
+ public static int[] createServerIcon(InputStream f) {
+ try {
+ return createServerIcon(ImageIO.read(f));
+ }catch(Throwable t) {
+ return null;
+ }
+ }
+
+ public static int[] createServerIcon(File f) {
+ try {
+ return createServerIcon(ImageIO.read(f));
+ }catch(Throwable t) {
+ return null;
+ }
+ }
+
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/ServerPing.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/ServerPing.java
new file mode 100644
index 0000000..de83dac
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/ServerPing.java
@@ -0,0 +1,107 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.api;
+
+import java.beans.ConstructorProperties;
+
+public class ServerPing {
+ private final byte protocolVersion;
+ private final String gameVersion;
+ private final String motd;
+ private final int currentPlayers;
+ private final int maxPlayers;
+
+ @ConstructorProperties({ "protocolVersion", "gameVersion", "motd", "currentPlayers", "maxPlayers" })
+ public ServerPing(final byte protocolVersion, final String gameVersion, final String motd, final int currentPlayers, final int maxPlayers) {
+ this.protocolVersion = protocolVersion;
+ this.gameVersion = gameVersion;
+ this.motd = motd;
+ this.currentPlayers = currentPlayers;
+ this.maxPlayers = maxPlayers;
+ }
+
+ public byte getProtocolVersion() {
+ return this.protocolVersion;
+ }
+
+ public String getGameVersion() {
+ return this.gameVersion;
+ }
+
+ public String getMotd() {
+ return this.motd;
+ }
+
+ public int getCurrentPlayers() {
+ return this.currentPlayers;
+ }
+
+ public int getMaxPlayers() {
+ return this.maxPlayers;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof ServerPing)) {
+ return false;
+ }
+ final ServerPing other = (ServerPing) o;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ if (this.getProtocolVersion() != other.getProtocolVersion()) {
+ return false;
+ }
+ final Object this$gameVersion = this.getGameVersion();
+ final Object other$gameVersion = other.getGameVersion();
+ Label_0078: {
+ if (this$gameVersion == null) {
+ if (other$gameVersion == null) {
+ break Label_0078;
+ }
+ } else if (this$gameVersion.equals(other$gameVersion)) {
+ break Label_0078;
+ }
+ return false;
+ }
+ final Object this$motd = this.getMotd();
+ final Object other$motd = other.getMotd();
+ if (this$motd == null) {
+ if (other$motd == null) {
+ return this.getCurrentPlayers() == other.getCurrentPlayers() && this.getMaxPlayers() == other.getMaxPlayers();
+ }
+ } else if (this$motd.equals(other$motd)) {
+ return this.getCurrentPlayers() == other.getCurrentPlayers() && this.getMaxPlayers() == other.getMaxPlayers();
+ }
+ return false;
+ }
+
+ public boolean canEqual(final Object other) {
+ return other instanceof ServerPing;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ result = result * 31 + this.getProtocolVersion();
+ final Object $gameVersion = this.getGameVersion();
+ result = result * 31 + (($gameVersion == null) ? 0 : $gameVersion.hashCode());
+ final Object $motd = this.getMotd();
+ result = result * 31 + (($motd == null) ? 0 : $motd.hashCode());
+ result = result * 31 + this.getCurrentPlayers();
+ result = result * 31 + this.getMaxPlayers();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "ServerPing(protocolVersion=" + this.getProtocolVersion() + ", gameVersion=" + this.getGameVersion() + ", motd=" + this.getMotd() + ", currentPlayers=" + this.getCurrentPlayers() + ", maxPlayers=" + this.getMaxPlayers()
+ + ")";
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/config/AuthServiceInfo.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/config/AuthServiceInfo.java
new file mode 100644
index 0000000..000d4dd
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/config/AuthServiceInfo.java
@@ -0,0 +1,48 @@
+package net.md_5.bungee.api.config;
+
+import java.util.List;
+
+public class AuthServiceInfo {
+
+ private final boolean enabled;
+ private final boolean registerEnabled;
+ private final String authfile;
+ private final int ipLimit;
+ private final List joinMessages;
+ private final int loginTimeout;
+
+ public AuthServiceInfo(boolean enabled, boolean registerEnabled, String authfile,
+ int timeout, List joinMessages, int loginTimeout) {
+ this.enabled = enabled;
+ this.registerEnabled = registerEnabled;
+ this.authfile = authfile;
+ this.ipLimit = timeout;
+ this.joinMessages = joinMessages;
+ this.loginTimeout = loginTimeout;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public boolean isRegisterEnabled() {
+ return registerEnabled;
+ }
+
+ public String getAuthfile() {
+ return authfile;
+ }
+
+ public int getIpLimit() {
+ return ipLimit;
+ }
+
+ public List getJoinMessages() {
+ return joinMessages;
+ }
+
+ public int getLoginTimeout() {
+ return loginTimeout;
+ }
+
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/config/ConfigurationAdapter.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/config/ConfigurationAdapter.java
new file mode 100644
index 0000000..03dac47
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/config/ConfigurationAdapter.java
@@ -0,0 +1,40 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.api.config;
+
+import java.util.Collection;
+import java.util.Map;
+
+public interface ConfigurationAdapter {
+ void load();
+
+ int getInt(final String p0, final int p1);
+
+ String getString(final String p0, final String p1);
+
+ boolean getBoolean(final String p0, final boolean p1);
+
+ Map getServers();
+
+ Collection getListeners();
+
+ Collection getGroups(final String p0);
+
+ Collection getPermissions(final String p0);
+
+ Collection getBlacklistURLs();
+
+ Collection getBlacklistSimpleWhitelist();
+
+ Collection getDisabledCommands();
+
+ Collection getICEServers();
+
+ AuthServiceInfo getAuthSettings();
+
+ Map getMap();
+
+ void forceSave();
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/config/ListenerInfo.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/config/ListenerInfo.java
new file mode 100644
index 0000000..bc9e244
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/config/ListenerInfo.java
@@ -0,0 +1,360 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.api.config;
+
+import java.io.File;
+import java.net.InetSocketAddress;
+import java.util.Map;
+
+import net.md_5.bungee.api.ServerIcon;
+import net.md_5.bungee.api.tab.TabListHandler;
+import net.md_5.bungee.eaglercraft.WebSocketRateLimiter;
+
+public class ListenerInfo {
+ private final String hostString;
+ private final InetSocketAddress host;
+ private final InetSocketAddress javaHost;
+ private final String motd;
+ private final int maxPlayers;
+ private final int tabListSize;
+ private final String defaultServer;
+ private final String fallbackServer;
+ private final boolean forceDefault;
+ private final boolean websocket;
+ private final boolean forwardIp;
+ private final String forwardIpHeader;
+ private final Map forcedHosts;
+ private final TexturePackInfo texturePack;
+ private final Class extends TabListHandler> tabList;
+ private final String serverIcon;
+ private final int[] serverIconCache;
+ private boolean serverIconLoaded;
+ private boolean serverIconSet;
+ private final boolean allowMOTD;
+ private final boolean allowQuery;
+ private final MOTDCacheConfiguration cacheConfig;
+ private final WebSocketRateLimiter rateLimitIP;
+ private final WebSocketRateLimiter rateLimitLogin;
+ private final WebSocketRateLimiter rateLimitMOTD;
+ private final WebSocketRateLimiter rateLimitQuery;
+
+
+ public ListenerInfo(final String hostString, final InetSocketAddress host, final InetSocketAddress javaHost,
+ final String forwardIpHeader, final String motd, final int maxPlayers, final int tabListSize,
+ final String defaultServer, final String fallbackServer, final boolean forceDefault,
+ final boolean websocket, final boolean forwardIp, final Map forcedHosts,
+ final TexturePackInfo texturePack, final Class extends TabListHandler> tabList, final String serverIcon,
+ final MOTDCacheConfiguration cacheConfig, final boolean allowMOTD, final boolean allowQuery,
+ final WebSocketRateLimiter rateLimitIP, final WebSocketRateLimiter rateLimitLogin,
+ final WebSocketRateLimiter rateLimitMOTD, final WebSocketRateLimiter rateLimitQuery) {
+ this.hostString = hostString;
+ this.host = host;
+ this.javaHost = javaHost;
+ this.motd = motd;
+ this.maxPlayers = maxPlayers;
+ this.tabListSize = tabListSize;
+ this.defaultServer = defaultServer;
+ this.fallbackServer = fallbackServer;
+ this.forceDefault = forceDefault;
+ this.websocket = websocket;
+ this.forwardIp = forwardIp;
+ this.forwardIpHeader = forwardIpHeader;
+ this.forcedHosts = forcedHosts;
+ this.texturePack = texturePack;
+ this.tabList = tabList;
+ this.serverIcon = serverIcon;
+ this.serverIconCache = new int[4096];
+ this.serverIconLoaded = false;
+ this.serverIconSet = false;
+ this.allowMOTD = allowMOTD;
+ this.allowQuery = allowQuery;
+ this.cacheConfig = cacheConfig;
+ this.rateLimitIP = rateLimitIP;
+ this.rateLimitLogin = rateLimitLogin;
+ this.rateLimitMOTD = rateLimitMOTD;
+ this.rateLimitQuery = rateLimitQuery;
+ }
+
+ public String getHostString() {
+ return this.hostString;
+ }
+
+ public InetSocketAddress getHost() {
+ return this.host;
+ }
+
+ public InetSocketAddress getJavaHost() {
+ return this.javaHost;
+ }
+
+ public String getMotd() {
+ return this.motd;
+ }
+
+ public int getMaxPlayers() {
+ return this.maxPlayers;
+ }
+
+ public int getTabListSize() {
+ return this.tabListSize;
+ }
+
+ public String getDefaultServer() {
+ return this.defaultServer;
+ }
+
+ public String getFallbackServer() {
+ return this.fallbackServer;
+ }
+
+ public boolean isForceDefault() {
+ return this.forceDefault;
+ }
+
+ public Map getForcedHosts() {
+ return this.forcedHosts;
+ }
+
+ public TexturePackInfo getTexturePack() {
+ return this.texturePack;
+ }
+
+ public Class extends TabListHandler> getTabList() {
+ return this.tabList;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof ListenerInfo)) {
+ return false;
+ }
+ final ListenerInfo other = (ListenerInfo) o;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ final Object this$host = this.getHost();
+ final Object other$host = other.getHost();
+ Label_0065: {
+ if (this$host == null) {
+ if (other$host == null) {
+ break Label_0065;
+ }
+ } else if (this$host.equals(other$host)) {
+ break Label_0065;
+ }
+ return false;
+ }
+ final Object this$motd = this.getMotd();
+ final Object other$motd = other.getMotd();
+ Label_0102: {
+ if (this$motd == null) {
+ if (other$motd == null) {
+ break Label_0102;
+ }
+ } else if (this$motd.equals(other$motd)) {
+ break Label_0102;
+ }
+ return false;
+ }
+ if (this.getMaxPlayers() != other.getMaxPlayers()) {
+ return false;
+ }
+ if (this.getTabListSize() != other.getTabListSize()) {
+ return false;
+ }
+ if (this.isWebsocket() != other.isWebsocket()) {
+ return false;
+ }
+ final Object this$defaultServer = this.getDefaultServer();
+ final Object other$defaultServer = other.getDefaultServer();
+ Label_0165: {
+ if (this$defaultServer == null) {
+ if (other$defaultServer == null) {
+ break Label_0165;
+ }
+ } else if (this$defaultServer.equals(other$defaultServer)) {
+ break Label_0165;
+ }
+ return false;
+ }
+ final Object this$fallbackServer = this.getFallbackServer();
+ final Object other$fallbackServer = other.getFallbackServer();
+ Label_0202: {
+ if (this$fallbackServer == null) {
+ if (other$fallbackServer == null) {
+ break Label_0202;
+ }
+ } else if (this$fallbackServer.equals(other$fallbackServer)) {
+ break Label_0202;
+ }
+ return false;
+ }
+ if (this.isForceDefault() != other.isForceDefault()) {
+ return false;
+ }
+ final Object this$forcedHosts = this.getForcedHosts();
+ final Object other$forcedHosts = other.getForcedHosts();
+ Label_0252: {
+ if (this$forcedHosts == null) {
+ if (other$forcedHosts == null) {
+ break Label_0252;
+ }
+ } else if (this$forcedHosts.equals(other$forcedHosts)) {
+ break Label_0252;
+ }
+ return false;
+ }
+ final Object this$texturePack = this.getTexturePack();
+ final Object other$texturePack = other.getTexturePack();
+ Label_0289: {
+ if (this$texturePack == null) {
+ if (other$texturePack == null) {
+ break Label_0289;
+ }
+ } else if (this$texturePack.equals(other$texturePack)) {
+ break Label_0289;
+ }
+ return false;
+ }
+ final Object this$tabList = this.getTabList();
+ final Object other$tabList = other.getTabList();
+ if (this$tabList == null) {
+ if (other$tabList == null) {
+ return true;
+ }
+ } else if (this$tabList.equals(other$tabList)) {
+ return true;
+ }
+ final Object this$getServerIcon = this.getServerIcon();
+ final Object other$getServerIcon = other.getServerIcon();
+ if (this$getServerIcon == null) {
+ if (other$getServerIcon == null) {
+ return true;
+ }
+ } else if (this$getServerIcon.equals(other$getServerIcon)) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean canEqual(final Object other) {
+ return other instanceof ListenerInfo;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ final Object $host = this.getHost();
+ result = result * 31 + (($host == null) ? 0 : $host.hashCode());
+ final Object $motd = this.getMotd();
+ result = result * 31 + (($motd == null) ? 0 : $motd.hashCode());
+ result = result * 31 + this.getMaxPlayers();
+ result = result * 31 + this.getTabListSize();
+ final Object $defaultServer = this.getDefaultServer();
+ result = result * 31 + (($defaultServer == null) ? 0 : $defaultServer.hashCode());
+ final Object $fallbackServer = this.getFallbackServer();
+ result = result * 31 + (($fallbackServer == null) ? 0 : $fallbackServer.hashCode());
+ result = result * 31 + (this.isForceDefault() ? 1231 : 1237);
+ final Object $forcedHosts = this.getForcedHosts();
+ result = result * 31 + (($forcedHosts == null) ? 0 : $forcedHosts.hashCode());
+ final Object $texturePack = this.getTexturePack();
+ result = result * 31 + (($texturePack == null) ? 0 : $texturePack.hashCode());
+ final Object $tabList = this.getTabList();
+ result = result * 31 + (($tabList == null) ? 0 : $tabList.hashCode());
+ final Object $serverIconCache = this.getTabList();
+ result = result * 31 + (($serverIconCache == null) ? 0 : $serverIconCache.hashCode());
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "ListenerInfo(host=" + this.getHost() + ", motd=" + this.getMotd() + ", maxPlayers=" + this.getMaxPlayers() + ", tabListSize=" + this.getTabListSize() + ", defaultServer=" + this.getDefaultServer() + ", fallbackServer="
+ + this.getFallbackServer() + ", forceDefault=" + this.isForceDefault() + ", websocket=" + this.isWebsocket() + ", forcedHosts=" + this.getForcedHosts() + ", texturePack=" + this.getTexturePack() + ", tabList=" + this.getTabList() + ")";
+ }
+
+ public boolean isWebsocket() {
+ return websocket;
+ }
+
+ public boolean hasForwardedHeaders() {
+ return forwardIp;
+ }
+
+ public String getForwardedIPHeader() {
+ return forwardIpHeader;
+ }
+
+ public String getServerIcon() {
+ return serverIcon;
+ }
+
+ public int[] getServerIconCache() {
+ if(!serverIconLoaded) {
+ if(serverIcon != null) {
+ int[] img = ServerIcon.createServerIcon(new File(serverIcon));
+ if(img != null) {
+ System.arraycopy(img, 0, serverIconCache, 0, img.length);
+ serverIconSet = true;
+ }else {
+ serverIconSet = false;
+ }
+ }else {
+ serverIconSet = false;
+ }
+ serverIconLoaded = true;
+ }
+ return serverIconCache;
+ }
+
+ public boolean isIconSet() {
+ getServerIconCache();
+ return serverIconSet;
+ }
+
+ public boolean isForwardIp() {
+ return forwardIp;
+ }
+
+ public boolean isServerIconLoaded() {
+ return serverIconLoaded;
+ }
+
+ public boolean isServerIconSet() {
+ return serverIconSet;
+ }
+
+ public boolean isAllowMOTD() {
+ return allowMOTD;
+ }
+
+ public boolean isAllowQuery() {
+ return allowQuery;
+ }
+
+ public MOTDCacheConfiguration getCacheConfig() {
+ return cacheConfig;
+ }
+
+ public WebSocketRateLimiter getRateLimitIP() {
+ return rateLimitIP;
+ }
+
+ public WebSocketRateLimiter getRateLimitLogin() {
+ return rateLimitLogin;
+ }
+
+ public WebSocketRateLimiter getRateLimitMOTD() {
+ return rateLimitMOTD;
+ }
+
+ public WebSocketRateLimiter getRateLimitQuery() {
+ return rateLimitQuery;
+ }
+
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/config/MOTDCacheConfiguration.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/config/MOTDCacheConfiguration.java
new file mode 100644
index 0000000..96dab1f
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/config/MOTDCacheConfiguration.java
@@ -0,0 +1,21 @@
+package net.md_5.bungee.api.config;
+
+public class MOTDCacheConfiguration {
+
+ public final int cacheTTL;
+ public final boolean cacheServerListAnimation;
+ public final boolean cacheServerListResults;
+ public final boolean cacheServerListTrending;
+ public final boolean cacheServerListPortfolios;
+
+ public MOTDCacheConfiguration(int cacheTTL, boolean cacheServerListAnimation, boolean cacheServerListResults,
+ boolean cacheServerListTrending, boolean cacheServerListPortfolios) {
+ this.cacheTTL = cacheTTL;
+ this.cacheServerListAnimation = cacheServerListAnimation;
+ this.cacheServerListResults = cacheServerListResults;
+ this.cacheServerListTrending = cacheServerListTrending;
+ this.cacheServerListPortfolios = cacheServerListPortfolios;
+ }
+
+
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/config/ServerInfo.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/config/ServerInfo.java
new file mode 100644
index 0000000..66e85f6
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/config/ServerInfo.java
@@ -0,0 +1,70 @@
+package net.md_5.bungee.api.config;
+
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import net.md_5.bungee.api.Callback;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.ServerPing;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+
+/**
+ * Class used to represent a server to connect to.
+ */
+public interface ServerInfo
+{
+
+ /**
+ * Get the name of this server.
+ *
+ * @return the configured name for this server address
+ */
+ String getName();
+
+ /**
+ * Gets the connectable host + port pair for this server. Implementations
+ * expect this to be used as the unique identifier per each instance of this
+ * class.
+ *
+ * @return the IP and port pair for this server
+ */
+ InetSocketAddress getAddress();
+
+ /**
+ * Get the set of all players on this server.
+ *
+ * @return an unmodifiable collection of all players on this server
+ */
+ Collection getPlayers();
+
+ /**
+ * Returns the MOTD which should be used when this server is a forced host.
+ *
+ * @return the motd
+ */
+
+ /**
+ * Whether the player can access this server. It will only return false when
+ * the player has no permission and this server is restricted.
+ *
+ * @param sender the player to check access for
+ * @return whether access is granted to this server
+ */
+ boolean canAccess(CommandSender sender);
+
+ /**
+ * Send data by any available means to this server.
+ *
+ * @param channel the channel to send this data via
+ * @param data the data to send
+ */
+ void sendData(String channel, byte[] data);
+
+ /**
+ * Asynchronously gets the current player count on this server.
+ *
+ * @param callback the callback to call when the count has been retrieved.
+ */
+ void ping(Callback callback);
+
+ String getRedirect();
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/config/TexturePackInfo.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/config/TexturePackInfo.java
new file mode 100644
index 0000000..5e8ade5
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/config/TexturePackInfo.java
@@ -0,0 +1,69 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.api.config;
+
+import java.beans.ConstructorProperties;
+
+public class TexturePackInfo {
+ private final String url;
+ private final int size;
+
+ @ConstructorProperties({ "url", "size" })
+ public TexturePackInfo(final String url, final int size) {
+ this.url = url;
+ this.size = size;
+ }
+
+ public String getUrl() {
+ return this.url;
+ }
+
+ public int getSize() {
+ return this.size;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof TexturePackInfo)) {
+ return false;
+ }
+ final TexturePackInfo other = (TexturePackInfo) o;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ final Object this$url = this.getUrl();
+ final Object other$url = other.getUrl();
+ if (this$url == null) {
+ if (other$url == null) {
+ return this.getSize() == other.getSize();
+ }
+ } else if (this$url.equals(other$url)) {
+ return this.getSize() == other.getSize();
+ }
+ return false;
+ }
+
+ public boolean canEqual(final Object other) {
+ return other instanceof TexturePackInfo;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ final Object $url = this.getUrl();
+ result = result * 31 + (($url == null) ? 0 : $url.hashCode());
+ result = result * 31 + this.getSize();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "TexturePackInfo(url=" + this.getUrl() + ", size=" + this.getSize() + ")";
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/connection/ConnectedPlayer.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/connection/ConnectedPlayer.java
new file mode 100644
index 0000000..cf09a65
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/connection/ConnectedPlayer.java
@@ -0,0 +1,8 @@
+package net.md_5.bungee.api.connection;
+
+/**
+ * Represents a player physically connected to the world hosted on this server.
+ */
+public interface ConnectedPlayer extends ProxiedPlayer
+{
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/connection/Connection.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/connection/Connection.java
new file mode 100644
index 0000000..f0a3f50
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/connection/Connection.java
@@ -0,0 +1,48 @@
+package net.md_5.bungee.api.connection;
+
+import java.net.InetSocketAddress;
+import net.md_5.bungee.protocol.packet.DefinedPacket;
+
+/**
+ * A proxy connection is defined as a connection directly connected to a socket.
+ * It should expose information about the remote peer, however not be specific
+ * to a type of connection, whether server or player.
+ */
+public interface Connection
+{
+
+ /**
+ * Gets the remote address of this connection.
+ *
+ * @return the remote address
+ */
+ InetSocketAddress getAddress();
+
+ /**
+ * Disconnects this end of the connection for the specified reason. If this
+ * is an {@link ProxiedPlayer} the respective server connection will be
+ * closed too.
+ *
+ * @param reason the reason shown to the player / sent to the server on
+ * disconnect
+ */
+ void disconnect(String reason);
+
+ /**
+ * Get the unsafe methods of this class.
+ *
+ * @return the unsafe method interface
+ */
+ Unsafe unsafe();
+
+ interface Unsafe
+ {
+
+ /**
+ * Send a packet to this connection.
+ *
+ * @param packet the packet to send
+ */
+ void sendPacket(DefinedPacket packet);
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/connection/PendingConnection.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/connection/PendingConnection.java
new file mode 100644
index 0000000..fba6e4f
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/connection/PendingConnection.java
@@ -0,0 +1,39 @@
+package net.md_5.bungee.api.connection;
+
+import java.net.InetSocketAddress;
+import net.md_5.bungee.api.config.ListenerInfo;
+
+/**
+ * Represents a user attempting to log into the proxy.
+ */
+public interface PendingConnection extends Connection
+{
+
+ /**
+ * Get the requested username.
+ *
+ * @return the requested username, or null if not set
+ */
+ String getName();
+
+ /**
+ * Get the numerical client version of the player attempting to log in.
+ *
+ * @return the protocol version of the remote client
+ */
+ byte getVersion();
+
+ /**
+ * Get the requested virtual host that the client tried to connect to.
+ *
+ * @return request virtual host or null if invalid / not specified.
+ */
+ InetSocketAddress getVirtualHost();
+
+ /**
+ * Get the listener that accepted this connection.
+ *
+ * @return the accepting listener
+ */
+ ListenerInfo getListener();
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java
new file mode 100644
index 0000000..6ef00b2
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java
@@ -0,0 +1,102 @@
+package net.md_5.bungee.api.connection;
+
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.config.ServerInfo;
+import net.md_5.bungee.api.tab.TabListHandler;
+
+/**
+ * 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
+{
+
+ /**
+ * 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);
+
+ /**
+ * Connects / transfers this user to the specified connection, gracefully
+ * closing the current one. Depending on the implementation, this method
+ * might return before the user has been connected.
+ *
+ * @param target the new server to connect to
+ */
+ void connect(ServerInfo target);
+
+ /**
+ * Gets the server this player is connected to.
+ *
+ * @return the server this player is connected to
+ */
+ Server getServer();
+
+ /**
+ * Gets the ping time between the proxy and this connection.
+ *
+ * @return the current ping time
+ */
+ int getPing();
+
+ /**
+ * Send a plugin message to this player.
+ *
+ * @param channel the channel to send this data via
+ * @param data the data to send
+ */
+ void sendData(String channel, byte[] data);
+
+ /**
+ * Get the pending connection that belongs to this player.
+ *
+ * @return the pending connection that this player used
+ */
+ PendingConnection getPendingConnection();
+
+ /**
+ * Make this player chat (say something), to the server he is currently on.
+ *
+ * @param message the message to say
+ */
+ void chat(String message);
+
+ /**
+ * Sets the new tab list for the user. At this stage it is not advisable to
+ * change after the user has logged in!
+ *
+ * @param list the new list
+ */
+ void setTabList(TabListHandler list);
+
+ /**
+ * Get the current tab list.
+ *
+ * @return the tab list in use by this user
+ */
+ TabListHandler getTabList();
+
+ /**
+ * Get the server which this player will be sent to next time the log in.
+ *
+ * @return the server, or null if default
+ */
+ ServerInfo getReconnectServer();
+
+ /**
+ * Set the server which this player will be sent to next time the log in.
+ *
+ * @param server the server to set
+ */
+ void setReconnectServer(ServerInfo server);
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/connection/Server.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/connection/Server.java
new file mode 100644
index 0000000..06eb6d1
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/connection/Server.java
@@ -0,0 +1,25 @@
+package net.md_5.bungee.api.connection;
+
+import net.md_5.bungee.api.config.ServerInfo;
+
+/**
+ * Represents a destination which this proxy might connect to.
+ */
+public interface Server extends Connection
+{
+
+ /**
+ * Returns the basic information about this server.
+ *
+ * @return the {@link ServerInfo} for this server
+ */
+ public ServerInfo getInfo();
+
+ /**
+ * Send data by any available means to this server.
+ *
+ * @param channel the channel to send this data via
+ * @param data the data to send
+ */
+ public abstract void sendData(String channel, byte[] data);
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/event/AsyncEvent.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/AsyncEvent.java
new file mode 100644
index 0000000..27eeeb1
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/AsyncEvent.java
@@ -0,0 +1,160 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.api.event;
+
+import java.beans.ConstructorProperties;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.google.common.base.Preconditions;
+
+import net.md_5.bungee.api.Callback;
+import net.md_5.bungee.api.plugin.Event;
+import net.md_5.bungee.api.plugin.Plugin;
+
+public class AsyncEvent extends Event {
+ private final Callback done;
+ private final Set intents;
+ private final AtomicBoolean fired;
+ private final AtomicInteger latch;
+
+ @Override
+ public void postCall() {
+ this.fired.set(true);
+ if (this.latch.get() == 0) {
+ this.done.done((T) this, null);
+ }
+ }
+
+ public void registerIntent(final Plugin plugin) {
+ Preconditions.checkState(!this.fired.get(), "Event %s has already been fired", new Object[] { this });
+ Preconditions.checkState(!this.intents.contains(plugin), "Plugin %s already registered intent for event %s", new Object[] { plugin, this });
+ this.intents.add(plugin);
+ this.latch.incrementAndGet();
+ }
+
+ public void completeIntent(final Plugin plugin) {
+ Preconditions.checkState(this.intents.contains(plugin), "Plugin %s has not registered intent for event %s", new Object[] { plugin, this });
+ this.intents.remove(plugin);
+ if (this.latch.decrementAndGet() == 0 && this.fired.get()) {
+ this.done.done((T) this, null);
+ }
+ }
+
+ @ConstructorProperties({ "done" })
+ public AsyncEvent(final Callback done) {
+ this.intents = Collections.newSetFromMap(new ConcurrentHashMap());
+ this.fired = new AtomicBoolean();
+ this.latch = new AtomicInteger();
+ this.done = done;
+ }
+
+ public Callback getDone() {
+ return this.done;
+ }
+
+ public Set getIntents() {
+ return this.intents;
+ }
+
+ public AtomicBoolean getFired() {
+ return this.fired;
+ }
+
+ public AtomicInteger getLatch() {
+ return this.latch;
+ }
+
+ @Override
+ public String toString() {
+ return "AsyncEvent(super=" + super.toString() + ", done=" + this.getDone() + ", intents=" + this.getIntents() + ", fired=" + this.getFired() + ", latch=" + this.getLatch() + ")";
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof AsyncEvent)) {
+ return false;
+ }
+ final AsyncEvent> other = (AsyncEvent>) o;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+ final Object this$done = this.getDone();
+ final Object other$done = other.getDone();
+ Label_0075: {
+ if (this$done == null) {
+ if (other$done == null) {
+ break Label_0075;
+ }
+ } else if (this$done.equals(other$done)) {
+ break Label_0075;
+ }
+ return false;
+ }
+ final Object this$intents = this.getIntents();
+ final Object other$intents = other.getIntents();
+ Label_0112: {
+ if (this$intents == null) {
+ if (other$intents == null) {
+ break Label_0112;
+ }
+ } else if (this$intents.equals(other$intents)) {
+ break Label_0112;
+ }
+ return false;
+ }
+ final Object this$fired = this.getFired();
+ final Object other$fired = other.getFired();
+ Label_0149: {
+ if (this$fired == null) {
+ if (other$fired == null) {
+ break Label_0149;
+ }
+ } else if (this$fired.equals(other$fired)) {
+ break Label_0149;
+ }
+ return false;
+ }
+ final Object this$latch = this.getLatch();
+ final Object other$latch = other.getLatch();
+ if (this$latch == null) {
+ if (other$latch == null) {
+ return true;
+ }
+ } else if (this$latch.equals(other$latch)) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean canEqual(final Object other) {
+ return other instanceof AsyncEvent;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ result = result * 31 + super.hashCode();
+ final Object $done = this.getDone();
+ result = result * 31 + (($done == null) ? 0 : $done.hashCode());
+ final Object $intents = this.getIntents();
+ result = result * 31 + (($intents == null) ? 0 : $intents.hashCode());
+ final Object $fired = this.getFired();
+ result = result * 31 + (($fired == null) ? 0 : $fired.hashCode());
+ final Object $latch = this.getLatch();
+ result = result * 31 + (($latch == null) ? 0 : $latch.hashCode());
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/event/ChatEvent.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/ChatEvent.java
new file mode 100644
index 0000000..eef6d50
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/ChatEvent.java
@@ -0,0 +1,91 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.api.event;
+
+import net.md_5.bungee.api.connection.Connection;
+import net.md_5.bungee.api.plugin.Cancellable;
+
+public class ChatEvent extends TargetedEvent implements Cancellable {
+ private boolean cancelled;
+ private String message;
+
+ public ChatEvent(final Connection sender, final Connection receiver, final String message) {
+ super(sender, receiver);
+ this.message = message;
+ }
+
+ public boolean isCommand() {
+ return this.message.length() > 0 && this.message.charAt(0) == '/';
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return this.cancelled;
+ }
+
+ public String getMessage() {
+ return this.message;
+ }
+
+ @Override
+ public void setCancelled(final boolean cancelled) {
+ this.cancelled = cancelled;
+ }
+
+ public void setMessage(final String message) {
+ this.message = message;
+ }
+
+ @Override
+ public String toString() {
+ return "ChatEvent(super=" + super.toString() + ", cancelled=" + this.isCancelled() + ", message=" + this.getMessage() + ")";
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof ChatEvent)) {
+ return false;
+ }
+ final ChatEvent other = (ChatEvent) o;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+ if (this.isCancelled() != other.isCancelled()) {
+ return false;
+ }
+ final Object this$message = this.getMessage();
+ final Object other$message = other.getMessage();
+ if (this$message == null) {
+ if (other$message == null) {
+ return true;
+ }
+ } else if (this$message.equals(other$message)) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean canEqual(final Object other) {
+ return other instanceof ChatEvent;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ result = result * 31 + super.hashCode();
+ result = result * 31 + (this.isCancelled() ? 1231 : 1237);
+ final Object $message = this.getMessage();
+ result = result * 31 + (($message == null) ? 0 : $message.hashCode());
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/event/LoginEvent.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/LoginEvent.java
new file mode 100644
index 0000000..c66d6df
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/LoginEvent.java
@@ -0,0 +1,103 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.api.event;
+
+import net.md_5.bungee.api.Callback;
+import net.md_5.bungee.api.connection.PendingConnection;
+import net.md_5.bungee.api.plugin.Cancellable;
+
+public class LoginEvent extends AsyncEvent implements Cancellable {
+ private boolean cancelled;
+ private String cancelReason;
+ private final PendingConnection connection;
+
+ public LoginEvent(final PendingConnection connection, final Callback done) {
+ super(done);
+ this.connection = connection;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return this.cancelled;
+ }
+
+ public String getCancelReason() {
+ return this.cancelReason;
+ }
+
+ public PendingConnection getConnection() {
+ return this.connection;
+ }
+
+ @Override
+ public void setCancelled(final boolean cancelled) {
+ this.cancelled = cancelled;
+ }
+
+ public void setCancelReason(final String cancelReason) {
+ this.cancelReason = cancelReason;
+ }
+
+ @Override
+ public String toString() {
+ return "LoginEvent(cancelled=" + this.isCancelled() + ", cancelReason=" + this.getCancelReason() + ", connection=" + this.getConnection() + ")";
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof LoginEvent)) {
+ return false;
+ }
+ final LoginEvent other = (LoginEvent) o;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ if (this.isCancelled() != other.isCancelled()) {
+ return false;
+ }
+ final Object this$cancelReason = this.getCancelReason();
+ final Object other$cancelReason = other.getCancelReason();
+ Label_0078: {
+ if (this$cancelReason == null) {
+ if (other$cancelReason == null) {
+ break Label_0078;
+ }
+ } else if (this$cancelReason.equals(other$cancelReason)) {
+ break Label_0078;
+ }
+ return false;
+ }
+ final Object this$connection = this.getConnection();
+ final Object other$connection = other.getConnection();
+ if (this$connection == null) {
+ if (other$connection == null) {
+ return true;
+ }
+ } else if (this$connection.equals(other$connection)) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean canEqual(final Object other) {
+ return other instanceof LoginEvent;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ result = result * 31 + (this.isCancelled() ? 1231 : 1237);
+ final Object $cancelReason = this.getCancelReason();
+ result = result * 31 + (($cancelReason == null) ? 0 : $cancelReason.hashCode());
+ final Object $connection = this.getConnection();
+ result = result * 31 + (($connection == null) ? 0 : $connection.hashCode());
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/event/PermissionCheckEvent.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/PermissionCheckEvent.java
new file mode 100644
index 0000000..6943c88
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/PermissionCheckEvent.java
@@ -0,0 +1,96 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.api.event;
+
+import java.beans.ConstructorProperties;
+
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.plugin.Event;
+
+public class PermissionCheckEvent extends Event {
+ private final CommandSender sender;
+ private final String permission;
+ private boolean hasPermission;
+
+ public boolean hasPermission() {
+ return this.hasPermission;
+ }
+
+ public CommandSender getSender() {
+ return this.sender;
+ }
+
+ public String getPermission() {
+ return this.permission;
+ }
+
+ public void setHasPermission(final boolean hasPermission) {
+ this.hasPermission = hasPermission;
+ }
+
+ @ConstructorProperties({ "sender", "permission", "hasPermission" })
+ public PermissionCheckEvent(final CommandSender sender, final String permission, final boolean hasPermission) {
+ this.sender = sender;
+ this.permission = permission;
+ this.hasPermission = hasPermission;
+ }
+
+ @Override
+ public String toString() {
+ return "PermissionCheckEvent(sender=" + this.getSender() + ", permission=" + this.getPermission() + ", hasPermission=" + this.hasPermission + ")";
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof PermissionCheckEvent)) {
+ return false;
+ }
+ final PermissionCheckEvent other = (PermissionCheckEvent) o;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ final Object this$sender = this.getSender();
+ final Object other$sender = other.getSender();
+ Label_0065: {
+ if (this$sender == null) {
+ if (other$sender == null) {
+ break Label_0065;
+ }
+ } else if (this$sender.equals(other$sender)) {
+ break Label_0065;
+ }
+ return false;
+ }
+ final Object this$permission = this.getPermission();
+ final Object other$permission = other.getPermission();
+ if (this$permission == null) {
+ if (other$permission == null) {
+ return this.hasPermission == other.hasPermission;
+ }
+ } else if (this$permission.equals(other$permission)) {
+ return this.hasPermission == other.hasPermission;
+ }
+ return false;
+ }
+
+ public boolean canEqual(final Object other) {
+ return other instanceof PermissionCheckEvent;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ final Object $sender = this.getSender();
+ result = result * 31 + (($sender == null) ? 0 : $sender.hashCode());
+ final Object $permission = this.getPermission();
+ result = result * 31 + (($permission == null) ? 0 : $permission.hashCode());
+ result = result * 31 + (this.hasPermission ? 1231 : 1237);
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/event/PlayerDisconnectEvent.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/PlayerDisconnectEvent.java
new file mode 100644
index 0000000..8e4a727
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/PlayerDisconnectEvent.java
@@ -0,0 +1,65 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.api.event;
+
+import java.beans.ConstructorProperties;
+
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.plugin.Event;
+
+public class PlayerDisconnectEvent extends Event {
+ private final ProxiedPlayer player;
+
+ @ConstructorProperties({ "player" })
+ public PlayerDisconnectEvent(final ProxiedPlayer player) {
+ this.player = player;
+ }
+
+ public ProxiedPlayer getPlayer() {
+ return this.player;
+ }
+
+ @Override
+ public String toString() {
+ return "PlayerDisconnectEvent(player=" + this.getPlayer() + ")";
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof PlayerDisconnectEvent)) {
+ return false;
+ }
+ final PlayerDisconnectEvent other = (PlayerDisconnectEvent) o;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ final Object this$player = this.getPlayer();
+ final Object other$player = other.getPlayer();
+ if (this$player == null) {
+ if (other$player == null) {
+ return true;
+ }
+ } else if (this$player.equals(other$player)) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean canEqual(final Object other) {
+ return other instanceof PlayerDisconnectEvent;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ final Object $player = this.getPlayer();
+ result = result * 31 + (($player == null) ? 0 : $player.hashCode());
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/event/PlayerHandshakeEvent.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/PlayerHandshakeEvent.java
new file mode 100644
index 0000000..89349e2
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/PlayerHandshakeEvent.java
@@ -0,0 +1,28 @@
+package net.md_5.bungee.api.event;
+
+import net.md_5.bungee.api.connection.PendingConnection;
+import net.md_5.bungee.protocol.packet.Packet2Handshake;
+import net.md_5.bungee.api.plugin.Event;
+
+/**
+ * Event called to represent a player first making their presence and username
+ * known.
+ */
+public class PlayerHandshakeEvent extends Event
+{
+
+ /**
+ * Connection attempting to login.
+ */
+ private final PendingConnection connection;
+ /**
+ * The handshake.
+ */
+ private final Packet2Handshake handshake;
+
+ public PlayerHandshakeEvent(PendingConnection connection, Packet2Handshake handshake)
+ {
+ this.connection = connection;
+ this.handshake = handshake;
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/event/PluginMessageEvent.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/PluginMessageEvent.java
new file mode 100644
index 0000000..de3f3e1
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/PluginMessageEvent.java
@@ -0,0 +1,92 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.api.event;
+
+import java.util.Arrays;
+
+import net.md_5.bungee.api.connection.Connection;
+import net.md_5.bungee.api.plugin.Cancellable;
+
+public class PluginMessageEvent extends TargetedEvent implements Cancellable {
+ private boolean cancelled;
+ private final String tag;
+ private final byte[] data;
+
+ public PluginMessageEvent(final Connection sender, final Connection receiver, final String tag, final byte[] data) {
+ super(sender, receiver);
+ this.tag = tag;
+ this.data = data;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return this.cancelled;
+ }
+
+ public String getTag() {
+ return this.tag;
+ }
+
+ public byte[] getData() {
+ return this.data;
+ }
+
+ @Override
+ public void setCancelled(final boolean cancelled) {
+ this.cancelled = cancelled;
+ }
+
+ @Override
+ public String toString() {
+ return "PluginMessageEvent(super=" + super.toString() + ", cancelled=" + this.isCancelled() + ", tag=" + this.getTag() + ", data=" + Arrays.toString(this.getData()) + ")";
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof PluginMessageEvent)) {
+ return false;
+ }
+ final PluginMessageEvent other = (PluginMessageEvent) o;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+ if (this.isCancelled() != other.isCancelled()) {
+ return false;
+ }
+ final Object this$tag = this.getTag();
+ final Object other$tag = other.getTag();
+ if (this$tag == null) {
+ if (other$tag == null) {
+ return Arrays.equals(this.getData(), other.getData());
+ }
+ } else if (this$tag.equals(other$tag)) {
+ return Arrays.equals(this.getData(), other.getData());
+ }
+ return false;
+ }
+
+ //@Override
+ public boolean canEqual(final Object other) {
+ return other instanceof PluginMessageEvent;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ result = result * 31 + super.hashCode();
+ result = result * 31 + (this.isCancelled() ? 1231 : 1237);
+ final Object $tag = this.getTag();
+ result = result * 31 + (($tag == null) ? 0 : $tag.hashCode());
+ result = result * 31 + Arrays.hashCode(this.getData());
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/event/PostLoginEvent.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/PostLoginEvent.java
new file mode 100644
index 0000000..d78b53a
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/PostLoginEvent.java
@@ -0,0 +1,61 @@
+package net.md_5.bungee.api.event;
+
+import java.beans.ConstructorProperties;
+
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.plugin.Event;
+
+public class PostLoginEvent extends Event {
+ private final ProxiedPlayer player;
+
+ @ConstructorProperties({ "player" })
+ public PostLoginEvent(final ProxiedPlayer player) {
+ this.player = player;
+ }
+
+ public ProxiedPlayer getPlayer() {
+ return this.player;
+ }
+
+ @Override
+ public String toString() {
+ return "PostLoginEvent(player=" + this.getPlayer() + ")";
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof PostLoginEvent)) {
+ return false;
+ }
+ final PostLoginEvent other = (PostLoginEvent) o;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ final Object this$player = this.getPlayer();
+ final Object other$player = other.getPlayer();
+ if (this$player == null) {
+ if (other$player == null) {
+ return true;
+ }
+ } else if (this$player.equals(other$player)) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean canEqual(final Object other) {
+ return other instanceof PostLoginEvent;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ final Object $player = this.getPlayer();
+ result = result * 31 + (($player == null) ? 0 : $player.hashCode());
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/event/ProxyPingEvent.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/ProxyPingEvent.java
new file mode 100644
index 0000000..a4052e0
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/ProxyPingEvent.java
@@ -0,0 +1,90 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.api.event;
+
+import java.beans.ConstructorProperties;
+
+import net.md_5.bungee.api.ServerPing;
+import net.md_5.bungee.api.connection.PendingConnection;
+import net.md_5.bungee.api.plugin.Event;
+
+public class ProxyPingEvent extends Event {
+ private final PendingConnection connection;
+ private ServerPing response;
+
+ public PendingConnection getConnection() {
+ return this.connection;
+ }
+
+ public ServerPing getResponse() {
+ return this.response;
+ }
+
+ public void setResponse(final ServerPing response) {
+ this.response = response;
+ }
+
+ @ConstructorProperties({ "connection", "response" })
+ public ProxyPingEvent(final PendingConnection connection, final ServerPing response) {
+ this.connection = connection;
+ this.response = response;
+ }
+
+ @Override
+ public String toString() {
+ return "ProxyPingEvent(connection=" + this.getConnection() + ", response=" + this.getResponse() + ")";
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof ProxyPingEvent)) {
+ return false;
+ }
+ final ProxyPingEvent other = (ProxyPingEvent) o;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ final Object this$connection = this.getConnection();
+ final Object other$connection = other.getConnection();
+ Label_0065: {
+ if (this$connection == null) {
+ if (other$connection == null) {
+ break Label_0065;
+ }
+ } else if (this$connection.equals(other$connection)) {
+ break Label_0065;
+ }
+ return false;
+ }
+ final Object this$response = this.getResponse();
+ final Object other$response = other.getResponse();
+ if (this$response == null) {
+ if (other$response == null) {
+ return true;
+ }
+ } else if (this$response.equals(other$response)) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean canEqual(final Object other) {
+ return other instanceof ProxyPingEvent;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ final Object $connection = this.getConnection();
+ result = result * 31 + (($connection == null) ? 0 : $connection.hashCode());
+ final Object $response = this.getResponse();
+ result = result * 31 + (($response == null) ? 0 : $response.hashCode());
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/event/ServerConnectEvent.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/ServerConnectEvent.java
new file mode 100644
index 0000000..22b7aca
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/ServerConnectEvent.java
@@ -0,0 +1,100 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.api.event;
+
+import net.md_5.bungee.api.config.ServerInfo;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.plugin.Cancellable;
+import net.md_5.bungee.api.plugin.Event;
+
+public class ServerConnectEvent extends Event implements Cancellable {
+ private final ProxiedPlayer player;
+ private ServerInfo target;
+ private boolean cancelled;
+
+ public ServerConnectEvent(final ProxiedPlayer player, final ServerInfo target) {
+ this.player = player;
+ this.target = target;
+ }
+
+ public ProxiedPlayer getPlayer() {
+ return this.player;
+ }
+
+ public ServerInfo getTarget() {
+ return this.target;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return this.cancelled;
+ }
+
+ public void setTarget(final ServerInfo target) {
+ this.target = target;
+ }
+
+ @Override
+ public void setCancelled(final boolean cancelled) {
+ this.cancelled = cancelled;
+ }
+
+ @Override
+ public String toString() {
+ return "ServerConnectEvent(player=" + this.getPlayer() + ", target=" + this.getTarget() + ", cancelled=" + this.isCancelled() + ")";
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof ServerConnectEvent)) {
+ return false;
+ }
+ final ServerConnectEvent other = (ServerConnectEvent) o;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ final Object this$player = this.getPlayer();
+ final Object other$player = other.getPlayer();
+ Label_0065: {
+ if (this$player == null) {
+ if (other$player == null) {
+ break Label_0065;
+ }
+ } else if (this$player.equals(other$player)) {
+ break Label_0065;
+ }
+ return false;
+ }
+ final Object this$target = this.getTarget();
+ final Object other$target = other.getTarget();
+ if (this$target == null) {
+ if (other$target == null) {
+ return this.isCancelled() == other.isCancelled();
+ }
+ } else if (this$target.equals(other$target)) {
+ return this.isCancelled() == other.isCancelled();
+ }
+ return false;
+ }
+
+ public boolean canEqual(final Object other) {
+ return other instanceof ServerConnectEvent;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ final Object $player = this.getPlayer();
+ result = result * 31 + (($player == null) ? 0 : $player.hashCode());
+ final Object $target = this.getTarget();
+ result = result * 31 + (($target == null) ? 0 : $target.hashCode());
+ result = result * 31 + (this.isCancelled() ? 1231 : 1237);
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/event/ServerConnectedEvent.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/ServerConnectedEvent.java
new file mode 100644
index 0000000..937be35
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/ServerConnectedEvent.java
@@ -0,0 +1,86 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.api.event;
+
+import java.beans.ConstructorProperties;
+
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.connection.Server;
+import net.md_5.bungee.api.plugin.Event;
+
+public class ServerConnectedEvent extends Event {
+ private final ProxiedPlayer player;
+ private final Server server;
+
+ @ConstructorProperties({ "player", "server" })
+ public ServerConnectedEvent(final ProxiedPlayer player, final Server server) {
+ this.player = player;
+ this.server = server;
+ }
+
+ public ProxiedPlayer getPlayer() {
+ return this.player;
+ }
+
+ public Server getServer() {
+ return this.server;
+ }
+
+ @Override
+ public String toString() {
+ return "ServerConnectedEvent(player=" + this.getPlayer() + ", server=" + this.getServer() + ")";
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof ServerConnectedEvent)) {
+ return false;
+ }
+ final ServerConnectedEvent other = (ServerConnectedEvent) o;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ final Object this$player = this.getPlayer();
+ final Object other$player = other.getPlayer();
+ Label_0065: {
+ if (this$player == null) {
+ if (other$player == null) {
+ break Label_0065;
+ }
+ } else if (this$player.equals(other$player)) {
+ break Label_0065;
+ }
+ return false;
+ }
+ final Object this$server = this.getServer();
+ final Object other$server = other.getServer();
+ if (this$server == null) {
+ if (other$server == null) {
+ return true;
+ }
+ } else if (this$server.equals(other$server)) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean canEqual(final Object other) {
+ return other instanceof ServerConnectedEvent;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ final Object $player = this.getPlayer();
+ result = result * 31 + (($player == null) ? 0 : $player.hashCode());
+ final Object $server = this.getServer();
+ result = result * 31 + (($server == null) ? 0 : $server.hashCode());
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/event/ServerKickEvent.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/ServerKickEvent.java
new file mode 100644
index 0000000..89c8cea
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/ServerKickEvent.java
@@ -0,0 +1,143 @@
+package net.md_5.bungee.api.event;
+
+import net.md_5.bungee.api.config.ServerInfo;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.plugin.Cancellable;
+import net.md_5.bungee.api.plugin.Event;
+
+/**
+ * Represents a player getting kicked from a server.
+ */
+
+public class ServerKickEvent extends Event implements Cancellable
+{
+
+ private boolean cancelled;
+ private final ProxiedPlayer player;
+ private String kickReason;
+ private ServerInfo cancelServer;
+ private State state;
+
+ public enum State
+ {
+
+ CONNECTING, CONNECTED, UNKNOWN;
+ }
+
+ public ServerKickEvent(ProxiedPlayer player, String kickReason, ServerInfo cancelServer)
+ {
+ this( player, kickReason, cancelServer, State.UNKNOWN );
+ }
+
+ public ServerKickEvent(ProxiedPlayer player, String kickReason, ServerInfo cancelServer, State state)
+ {
+ this.player = player;
+ this.kickReason = kickReason;
+ this.cancelServer = cancelServer;
+ this.state = state;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return this.cancelled;
+ }
+
+ public ProxiedPlayer getPlayer() {
+ return this.player;
+ }
+
+ public String getKickReason() {
+ return this.kickReason;
+ }
+
+ public ServerInfo getCancelServer() {
+ return this.cancelServer;
+ }
+
+ @Override
+ public void setCancelled(final boolean cancelled) {
+ this.cancelled = cancelled;
+ }
+
+ public void setKickReason(final String kickReason) {
+ this.kickReason = kickReason;
+ }
+
+ public void setCancelServer(final ServerInfo cancelServer) {
+ this.cancelServer = cancelServer;
+ }
+
+ @Override
+ public String toString() {
+ return "ServerKickEvent(cancelled=" + this.isCancelled() + ", player=" + this.getPlayer() + ", kickReason=" + this.getKickReason() + ", cancelServer=" + this.getCancelServer() + ")";
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof ServerKickEvent)) {
+ return false;
+ }
+ final ServerKickEvent other = (ServerKickEvent) o;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ if (this.isCancelled() != other.isCancelled()) {
+ return false;
+ }
+ final Object this$player = this.getPlayer();
+ final Object other$player = other.getPlayer();
+ Label_0078: {
+ if (this$player == null) {
+ if (other$player == null) {
+ break Label_0078;
+ }
+ } else if (this$player.equals(other$player)) {
+ break Label_0078;
+ }
+ return false;
+ }
+ final Object this$kickReason = this.getKickReason();
+ final Object other$kickReason = other.getKickReason();
+ Label_0115: {
+ if (this$kickReason == null) {
+ if (other$kickReason == null) {
+ break Label_0115;
+ }
+ } else if (this$kickReason.equals(other$kickReason)) {
+ break Label_0115;
+ }
+ return false;
+ }
+ final Object this$cancelServer = this.getCancelServer();
+ final Object other$cancelServer = other.getCancelServer();
+ if (this$cancelServer == null) {
+ if (other$cancelServer == null) {
+ return true;
+ }
+ } else if (this$cancelServer.equals(other$cancelServer)) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean canEqual(final Object other) {
+ return other instanceof ServerKickEvent;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ result = result * 31 + (this.isCancelled() ? 1231 : 1237);
+ final Object $player = this.getPlayer();
+ result = result * 31 + (($player == null) ? 0 : $player.hashCode());
+ final Object $kickReason = this.getKickReason();
+ result = result * 31 + (($kickReason == null) ? 0 : $kickReason.hashCode());
+ final Object $cancelServer = this.getCancelServer();
+ result = result * 31 + (($cancelServer == null) ? 0 : $cancelServer.hashCode());
+ return result;
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/event/ServerSwitchEvent.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/ServerSwitchEvent.java
new file mode 100644
index 0000000..ce3753a
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/ServerSwitchEvent.java
@@ -0,0 +1,65 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.api.event;
+
+import java.beans.ConstructorProperties;
+
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.plugin.Event;
+
+public class ServerSwitchEvent extends Event {
+ private final ProxiedPlayer player;
+
+ @ConstructorProperties({ "player" })
+ public ServerSwitchEvent(final ProxiedPlayer player) {
+ this.player = player;
+ }
+
+ public ProxiedPlayer getPlayer() {
+ return this.player;
+ }
+
+ @Override
+ public String toString() {
+ return "ServerSwitchEvent(player=" + this.getPlayer() + ")";
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof ServerSwitchEvent)) {
+ return false;
+ }
+ final ServerSwitchEvent other = (ServerSwitchEvent) o;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ final Object this$player = this.getPlayer();
+ final Object other$player = other.getPlayer();
+ if (this$player == null) {
+ if (other$player == null) {
+ return true;
+ }
+ } else if (this$player.equals(other$player)) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean canEqual(final Object other) {
+ return other instanceof ServerSwitchEvent;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ final Object $player = this.getPlayer();
+ result = result * 31 + (($player == null) ? 0 : $player.hashCode());
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/event/TargetedEvent.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/TargetedEvent.java
new file mode 100644
index 0000000..07d7028
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/TargetedEvent.java
@@ -0,0 +1,85 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.api.event;
+
+import java.beans.ConstructorProperties;
+
+import net.md_5.bungee.api.connection.Connection;
+import net.md_5.bungee.api.plugin.Event;
+
+public abstract class TargetedEvent extends Event {
+ private final Connection sender;
+ private final Connection receiver;
+
+ public Connection getSender() {
+ return this.sender;
+ }
+
+ public Connection getReceiver() {
+ return this.receiver;
+ }
+
+ @Override
+ public String toString() {
+ return "TargetedEvent(sender=" + this.getSender() + ", receiver=" + this.getReceiver() + ")";
+ }
+
+ @ConstructorProperties({ "sender", "receiver" })
+ public TargetedEvent(final Connection sender, final Connection receiver) {
+ this.sender = sender;
+ this.receiver = receiver;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof TargetedEvent)) {
+ return false;
+ }
+ final TargetedEvent other = (TargetedEvent) o;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ final Object this$sender = this.getSender();
+ final Object other$sender = other.getSender();
+ Label_0065: {
+ if (this$sender == null) {
+ if (other$sender == null) {
+ break Label_0065;
+ }
+ } else if (this$sender.equals(other$sender)) {
+ break Label_0065;
+ }
+ return false;
+ }
+ final Object this$receiver = this.getReceiver();
+ final Object other$receiver = other.getReceiver();
+ if (this$receiver == null) {
+ if (other$receiver == null) {
+ return true;
+ }
+ } else if (this$receiver.equals(other$receiver)) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean canEqual(final Object other) {
+ return other instanceof TargetedEvent;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ final Object $sender = this.getSender();
+ result = result * 31 + (($sender == null) ? 0 : $sender.hashCode());
+ final Object $receiver = this.getReceiver();
+ result = result * 31 + (($receiver == null) ? 0 : $receiver.hashCode());
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/event/WebsocketMOTDEvent.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/WebsocketMOTDEvent.java
new file mode 100644
index 0000000..3c665f0
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/WebsocketMOTDEvent.java
@@ -0,0 +1,15 @@
+package net.md_5.bungee.api.event;
+
+import net.md_5.bungee.api.MOTD;
+
+public class WebsocketMOTDEvent extends WebsocketQueryEvent {
+
+ public WebsocketMOTDEvent(MOTD connection) {
+ super(connection);
+ }
+
+ public MOTD getMOTD() {
+ return (MOTD)connection;
+ }
+
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/event/WebsocketQueryEvent.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/WebsocketQueryEvent.java
new file mode 100644
index 0000000..52ba23b
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/event/WebsocketQueryEvent.java
@@ -0,0 +1,33 @@
+package net.md_5.bungee.api.event;
+
+import java.net.InetAddress;
+
+import net.md_5.bungee.api.QueryConnection;
+import net.md_5.bungee.api.config.ListenerInfo;
+import net.md_5.bungee.api.plugin.Event;
+
+public class WebsocketQueryEvent extends Event {
+
+ protected final QueryConnection connection;
+
+ public WebsocketQueryEvent(QueryConnection connection) {
+ this.connection = connection;
+ }
+
+ public InetAddress getRemoteAddress() {
+ return connection.getRemoteAddress();
+ }
+
+ public ListenerInfo getListener() {
+ return connection.getListener();
+ }
+
+ public String getAccept() {
+ return connection.getAccept();
+ }
+
+ public QueryConnection getQuery() {
+ return connection;
+ }
+
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/Cancellable.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/Cancellable.java
new file mode 100644
index 0000000..2b04a2b
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/Cancellable.java
@@ -0,0 +1,23 @@
+package net.md_5.bungee.api.plugin;
+
+/**
+ * Events that implement this indicate that they may be cancelled and thus
+ * prevented from happening.
+ */
+public interface Cancellable
+{
+
+ /**
+ * Get whether or not this event is cancelled.
+ *
+ * @return the cancelled state of this event
+ */
+ public boolean isCancelled();
+
+ /**
+ * Sets the cancelled state of this event.
+ *
+ * @param cancel the state to set
+ */
+ public void setCancelled(boolean cancel);
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/Command.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/Command.java
new file mode 100644
index 0000000..67494bc
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/Command.java
@@ -0,0 +1,99 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.api.plugin;
+
+import java.util.Arrays;
+
+import com.google.common.base.Preconditions;
+
+import net.md_5.bungee.api.CommandSender;
+
+public abstract class Command {
+ private final String name;
+ private final String permission;
+ private final String[] aliases;
+
+ public Command(final String name) {
+ this(name, null, new String[0]);
+ }
+
+ public Command(final String name, final String permission, final String... aliases) {
+ Preconditions.checkArgument(name != null, (Object) "name");
+ this.name = name;
+ this.permission = permission;
+ this.aliases = aliases;
+ }
+
+ public abstract void execute(final CommandSender p0, final String[] p1);
+
+ public String getName() {
+ return this.name;
+ }
+
+ public String getPermission() {
+ return this.permission;
+ }
+
+ public String[] getAliases() {
+ return this.aliases;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof Command)) {
+ return false;
+ }
+ final Command other = (Command) o;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ final Object this$name = this.getName();
+ final Object other$name = other.getName();
+ Label_0065: {
+ if (this$name == null) {
+ if (other$name == null) {
+ break Label_0065;
+ }
+ } else if (this$name.equals(other$name)) {
+ break Label_0065;
+ }
+ return false;
+ }
+ final Object this$permission = this.getPermission();
+ final Object other$permission = other.getPermission();
+ if (this$permission == null) {
+ if (other$permission == null) {
+ return Arrays.deepEquals(this.getAliases(), other.getAliases());
+ }
+ } else if (this$permission.equals(other$permission)) {
+ return Arrays.deepEquals(this.getAliases(), other.getAliases());
+ }
+ return false;
+ }
+
+ public boolean canEqual(final Object other) {
+ return other instanceof Command;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ final Object $name = this.getName();
+ result = result * 31 + (($name == null) ? 0 : $name.hashCode());
+ final Object $permission = this.getPermission();
+ result = result * 31 + (($permission == null) ? 0 : $permission.hashCode());
+ result = result * 31 + Arrays.deepHashCode(this.getAliases());
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Command(name=" + this.getName() + ", permission=" + this.getPermission() + ", aliases=" + Arrays.deepToString(this.getAliases()) + ")";
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/Event.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/Event.java
new file mode 100644
index 0000000..e2e01ec
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/Event.java
@@ -0,0 +1,15 @@
+package net.md_5.bungee.api.plugin;
+
+/**
+ * Dummy class which all callable events must extend.
+ */
+public abstract class Event
+{
+
+ /**
+ * Method called after this event has been dispatched to all handlers.
+ */
+ public void postCall()
+ {
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/Listener.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/Listener.java
new file mode 100644
index 0000000..31ed4ee
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/Listener.java
@@ -0,0 +1,8 @@
+package net.md_5.bungee.api.plugin;
+
+/**
+ * Dummy interface which all event subscribers and listeners must implement.
+ */
+public interface Listener
+{
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/Plugin.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/Plugin.java
new file mode 100644
index 0000000..2b66e1b
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/Plugin.java
@@ -0,0 +1,101 @@
+package net.md_5.bungee.api.plugin;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.logging.Logger;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.config.ConfigurationAdapter;
+
+/**
+ * Represents any Plugin that may be loaded at runtime to enhance existing
+ * functionality.
+ */
+public class Plugin
+{
+ private PluginDescription description;
+ private ProxyServer proxy;
+ private File file;
+ private Logger logger;
+
+ protected Plugin() {
+ }
+
+ protected Plugin(PluginDescription forceDesc) {
+ this.description = forceDesc;
+ }
+
+ public PluginDescription getDescription() {
+ return description;
+ }
+
+ public ProxyServer getProxy() {
+ return proxy;
+ }
+
+ public File getFile() {
+ return file;
+ }
+
+ public Logger getLogger() {
+ return logger;
+ }
+
+ /**
+ * Called when the plugin has just been loaded. Most of the proxy will not
+ * be initialized, so only use it for registering
+ * {@link ConfigurationAdapter}'s and other predefined behavior.
+ */
+ public void onLoad()
+ {
+ }
+
+ /**
+ * Called when this plugin is enabled.
+ */
+ public void onEnable()
+ {
+ }
+
+ /**
+ * Called when this plugin is disabled.
+ */
+ public void onDisable()
+ {
+ }
+
+ /**
+ * Gets the data folder where this plugin may store arbitrary data. It will
+ * be a child of {@link ProxyServer#getPluginsFolder()}.
+ *
+ * @return the data folder of this plugin
+ */
+ public final File getDataFolder()
+ {
+ return new File( getProxy().getPluginsFolder(), getDescription().getName() );
+ }
+
+ /**
+ * Get a resource from within this plugins jar or container. Care must be
+ * taken to close the returned stream.
+ *
+ * @param name the full path name of this resource
+ * @return the stream for getting this resource, or null if it does not
+ * exist
+ */
+ public final InputStream getResourceAsStream(String name)
+ {
+ return getClass().getClassLoader().getResourceAsStream( name );
+ }
+
+ /**
+ * Called by the loader to initialize the fields in this plugin.
+ *
+ */
+ final void init(ProxyServer proxy, PluginDescription description)
+ {
+ this.proxy = proxy;
+ this.description = description;
+ this.file = description.getFile();
+ this.logger = new PluginLogger( this );
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/PluginClassloader.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/PluginClassloader.java
new file mode 100644
index 0000000..85203fb
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/PluginClassloader.java
@@ -0,0 +1,55 @@
+package net.md_5.bungee.api.plugin;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+public class PluginClassloader extends URLClassLoader
+{
+
+ private static final Set allLoaders = new CopyOnWriteArraySet<>();
+
+ static
+ {
+ ClassLoader.registerAsParallelCapable();
+ }
+
+ public PluginClassloader(URL[] urls)
+ {
+ super( urls );
+ allLoaders.add( this );
+ }
+
+ @Override
+ protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException
+ {
+ return loadClass0( name, resolve, true );
+ }
+
+ 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 )
+ {
+ }
+ }
+ }
+ }
+ throw new ClassNotFoundException( name );
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/PluginDescription.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/PluginDescription.java
new file mode 100644
index 0000000..5589a37
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/PluginDescription.java
@@ -0,0 +1,99 @@
+package net.md_5.bungee.api.plugin;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
+/**
+ * POJO representing the plugin.yml file.
+ */
+
+//@Data
+
+public class PluginDescription
+{
+ public PluginDescription(){
+
+ }
+
+ public PluginDescription(String name, String main, String version, String author, Set depends, File file) {
+ this.name = name;
+ this.main = main;
+ this.version = version;
+ this.author = author;
+ this.depends = depends;
+ this.file = file;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getMain() {
+ return main;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public String getAuthor() {
+ return author;
+ }
+
+ public Set getDepends() {
+ return depends;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setMain(String main) {
+ this.main = main;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public void setAuthor(String author) {
+ this.author = author;
+ }
+
+ public void setDepends(Set depends) {
+ this.depends = depends;
+ }
+
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+ /**
+ * Friendly name of the plugin.
+ */
+ private String name;
+ /**
+ * Plugin main class. Needs to extend {@link Plugin}.
+ */
+ private String main;
+ /**
+ * Plugin version.
+ */
+ private String version;
+ /**
+ * Plugin author.
+ */
+ private String author;
+ /**
+ * Plugin hard dependencies.
+ */
+ private Set depends = new HashSet<>();
+ /**
+ * File we were loaded from.
+ */
+ private File file = null;
+
+ public File getFile() {
+ return file;
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/PluginLogger.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/PluginLogger.java
new file mode 100644
index 0000000..118fb71
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/PluginLogger.java
@@ -0,0 +1,24 @@
+package net.md_5.bungee.api.plugin;
+
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import net.md_5.bungee.api.ProxyServer;
+
+public class PluginLogger extends Logger
+{
+
+ private String pluginName;
+
+ protected PluginLogger(Plugin plugin)
+ {
+ super( plugin.getClass().getCanonicalName(), null );
+ pluginName = "[" + plugin.getDescription().getName() + "] ";
+ }
+
+ @Override
+ public void log(LogRecord logRecord)
+ {
+ logRecord.setMessage( pluginName + logRecord.getMessage() );
+ ProxyServer.getInstance().getLogger().log( logRecord );
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/PluginManager.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/PluginManager.java
new file mode 100644
index 0000000..c0cd4f4
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/PluginManager.java
@@ -0,0 +1,398 @@
+package net.md_5.bungee.api.plugin;
+
+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.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.logging.Level;
+import java.util.regex.Pattern;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.event.EventBus;
+import net.md_5.bungee.event.EventHandler;
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * Class to manage bridging between plugin duties and implementation duties, for
+ * example event handling and plugin management.
+ */
+public class PluginManager
+{
+
+ private static final Pattern argsSplit = Pattern.compile( " " );
+ /*========================================================================*/
+ private final ProxyServer proxy;
+ /*========================================================================*/
+ private final Yaml yaml = new Yaml();
+ private final EventBus eventBus;
+ private final Map plugins = new LinkedHashMap<>();
+ private final Map commandMap = new HashMap<>();
+ private Map toLoad = new HashMap<>();
+ private Multimap commandsByPlugin = ArrayListMultimap.create();
+ private Multimap listenersByPlugin = ArrayListMultimap.create();
+
+ @SuppressWarnings("unchecked")
+ public PluginManager(ProxyServer proxy)
+ {
+ this.proxy = proxy;
+ eventBus = new EventBus( proxy.getLogger() );
+ }
+
+ /**
+ * Register a command so that it may be executed.
+ *
+ * @param plugin the plugin owning this command
+ * @param command the command to register
+ */
+ public void registerCommand(Plugin plugin, Command command)
+ {
+ commandMap.put( command.getName().toLowerCase(), command );
+ for ( String alias : command.getAliases() )
+ {
+ commandMap.put( alias.toLowerCase(), command );
+ }
+ commandsByPlugin.put( plugin, command );
+ }
+
+ /**
+ * Unregister a command so it will no longer be executed.
+ *
+ * @param command the command to unregister
+ */
+ public void unregisterCommand(Command command)
+ {
+ commandMap.values().remove( command );
+ commandsByPlugin.values().remove( command );
+ }
+
+ /**
+ * Unregister all commands owned by a {@link Plugin}
+ *
+ * @param plugin the plugin to register the commands of
+ */
+ public void unregisterCommands(Plugin plugin)
+ {
+ for ( Iterator it = commandsByPlugin.get( plugin ).iterator(); it.hasNext(); )
+ {
+ commandMap.values().remove( it.next() );
+ it.remove();
+ }
+ }
+
+ public boolean dispatchCommand(CommandSender sender, String commandLine)
+ {
+ return dispatchCommand( sender, commandLine, null );
+ }
+
+ /**
+ * Execute a command if it is registered, else return false.
+ *
+ * @param sender the sender executing the command
+ * @param commandLine the complete command line including command name and
+ * arguments
+ * @return whether the command was handled
+ */
+ public boolean dispatchCommand(CommandSender sender, String commandLine, List tabResults)
+ {
+ String[] split = argsSplit.split( commandLine );
+ // Check for chat that only contains " "
+ if ( split.length == 0 )
+ {
+ return false;
+ }
+
+ String commandName = split[0].toLowerCase();
+ if ( proxy.getDisabledCommands().contains( commandName ) )
+ {
+ return false;
+ }
+ Command command = commandMap.get( commandName );
+ if ( command == null )
+ {
+ return false;
+ }
+
+ String permission = command.getPermission();
+ if ( permission != null && !permission.isEmpty() && !sender.hasPermission( permission ) )
+ {
+ sender.sendMessage( proxy.getTranslation( "no_permission" ) );
+ return true;
+ }
+
+ String[] args = Arrays.copyOfRange( split, 1, split.length );
+ try
+ {
+ if ( tabResults == null )
+ {
+ command.execute( sender, args );
+ } else if ( command instanceof TabExecutor )
+ {
+ for ( String s : ( (TabExecutor) command ).onTabComplete( sender, args ) )
+ {
+ tabResults.add( s );
+ }
+ }
+ } catch ( Exception ex )
+ {
+ sender.sendMessage( ChatColor.RED + "An internal error occurred whilst executing this command, please check the console log for details." );
+ ProxyServer.getInstance().getLogger().log( Level.WARNING, "Error in dispatching command", ex );
+ }
+ return true;
+ }
+
+ /**
+ * Returns the {@link Plugin} objects corresponding to all loaded plugins.
+ *
+ * @return the set of loaded plugins
+ */
+ public Collection getPlugins()
+ {
+ return plugins.values();
+ }
+
+ /**
+ * Returns a loaded plugin identified by the specified name.
+ *
+ * @param name of the plugin to retrieve
+ * @return the retrieved plugin or null if not loaded
+ */
+ public Plugin getPlugin(String name)
+ {
+ return plugins.get( name );
+ }
+
+ public void loadAndEnablePlugins()
+ {
+ Map pluginStatuses = new HashMap<>();
+ for ( Map.Entry entry : toLoad.entrySet() )
+ {
+ PluginDescription plugin = entry.getValue();
+ if ( !enablePlugin( pluginStatuses, new Stack(), plugin ) )
+ {
+ ProxyServer.getInstance().getLogger().warning( "Failed to enable " + entry.getKey() );
+ }
+ }
+ toLoad.clear();
+ toLoad = null;
+
+ for ( Plugin plugin : plugins.values() )
+ {
+ try
+ {
+ plugin.onEnable();
+ 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 );
+ }
+ }
+ }
+
+ private boolean enablePlugin(Map pluginStatuses, Stack dependStack, PluginDescription plugin)
+ {
+ if ( pluginStatuses.containsKey( plugin ) )
+ {
+ return pluginStatuses.get( plugin );
+ }
+
+ // success status
+ boolean status = true;
+
+ // try to load dependencies first
+ for ( String dependName : plugin.getDepends() )
+ {
+ PluginDescription depend = toLoad.get( dependName );
+ Boolean dependStatus = ( depend != null ) ? pluginStatuses.get( depend ) : Boolean.FALSE;
+
+ if ( dependStatus == null )
+ {
+ if ( dependStack.contains( depend ) )
+ {
+ StringBuilder dependencyGraph = new StringBuilder();
+ for ( PluginDescription element : dependStack )
+ {
+ dependencyGraph.append( element.getName() ).append( " -> " );
+ }
+ dependencyGraph.append( plugin.getName() ).append( " -> " ).append( dependName );
+ ProxyServer.getInstance().getLogger().log( Level.WARNING, "Circular dependency detected: " + dependencyGraph );
+ status = false;
+ } else
+ {
+ dependStack.push( plugin );
+ dependStatus = this.enablePlugin( pluginStatuses, dependStack, depend );
+ dependStack.pop();
+ }
+ }
+
+ if ( dependStatus == Boolean.FALSE )
+ {
+ ProxyServer.getInstance().getLogger().log( Level.WARNING, "{0} (required by {1}) is unavailable", new Object[]
+ {
+ String.valueOf( depend.getName() ), plugin.getName()
+ } );
+ status = false;
+ }
+
+ if ( !status )
+ {
+ break;
+ }
+ }
+
+ // do actual loading
+ if ( status )
+ {
+ try
+ {
+ URLClassLoader loader = new PluginClassloader( new URL[]
+ {
+ plugin.getFile().toURI().toURL()
+ } );
+ Class> main = loader.loadClass( plugin.getMain() );
+ Plugin clazz = (Plugin) main.getDeclaredConstructor().newInstance();
+
+ clazz.init( proxy, plugin );
+ plugins.put( plugin.getName(), clazz );
+ clazz.onLoad();
+ 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 );
+ }
+ }
+
+ pluginStatuses.put( plugin, status );
+ return status;
+ }
+
+ public void addInternalPlugin(Plugin plug) {
+ this.plugins.put(plug.getDescription().getName(), plug);
+ plug.init(proxy, plug.getDescription());
+ }
+
+ /**
+ * Load all plugins from the specified folder.
+ *
+ * @param folder the folder to search for plugins in
+ */
+ public void detectPlugins(File folder)
+ {
+ Preconditions.checkNotNull( folder, "folder" );
+ Preconditions.checkArgument( folder.isDirectory(), "Must load from a directory" );
+
+ for ( File file : folder.listFiles() )
+ {
+ if ( file.isFile() && file.getName().endsWith( ".jar" ) )
+ {
+ try ( JarFile jar = new JarFile( file ) )
+ {
+ JarEntry pdf = jar.getJarEntry( "plugin.yml" );
+ Preconditions.checkNotNull( pdf, "Plugin must have a plugin.yml" );
+
+ try ( InputStream in = jar.getInputStream( pdf ) )
+ {
+ PluginDescription desc = yaml.loadAs( in, PluginDescription.class );
+ desc.setFile( file );
+ toLoad.put( desc.getName(), desc );
+ }
+ } catch ( Exception ex )
+ {
+ ProxyServer.getInstance().getLogger().log( Level.WARNING, "Could not load plugin from file " + file, ex );
+ }
+ }
+ }
+ }
+
+ /**
+ * Dispatch an event to all subscribed listeners and return the event once
+ * it has been handled by these listeners.
+ *
+ * @param the type bounds, must be a class which extends event
+ * @param event the event to call
+ * @return the called event
+ */
+ public T callEvent(T event)
+ {
+ Preconditions.checkNotNull( event, "event" );
+
+ long start = System.nanoTime();
+ eventBus.post( event );
+ event.postCall();
+
+ long elapsed = start - System.nanoTime();
+ if ( elapsed > 250000 )
+ {
+ ProxyServer.getInstance().getLogger().log( Level.WARNING, "Event {0} took more {1}ns to process!", new Object[]
+ {
+ event, elapsed
+ } );
+ }
+ return event;
+ }
+
+ /**
+ * Register a {@link Listener} for receiving called events. Methods in this
+ * Object which wish to receive events must be annotated with the
+ * {@link EventHandler} annotation.
+ *
+ * @param plugin the owning plugin
+ * @param listener the listener to register events for
+ */
+ 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 );
+ }
+
+ /**
+ * Unregister a {@link Listener} so that the events do not reach it anymore.
+ *
+ * @param listener the listener to unregister
+ */
+ public void unregisterListener(Listener listener)
+ {
+ eventBus.unregister( listener );
+ listenersByPlugin.values().remove( listener );
+ }
+
+ /**
+ * Unregister all of a Plugin's listener.
+ *
+ * @param plugin
+ */
+ public void unregisterListeners(Plugin plugin)
+ {
+ for ( Iterator it = listenersByPlugin.get( plugin ).iterator(); it.hasNext(); )
+ {
+ eventBus.unregister( it.next() );
+ it.remove();
+ }
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/TabExecutor.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/TabExecutor.java
new file mode 100644
index 0000000..cdf5174
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/plugin/TabExecutor.java
@@ -0,0 +1,10 @@
+package net.md_5.bungee.api.plugin;
+
+import net.md_5.bungee.api.CommandSender;
+
+
+public interface TabExecutor
+{
+
+ public Iterable onTabComplete(CommandSender sender, String[] args);
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/scheduler/ScheduledTask.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/scheduler/ScheduledTask.java
new file mode 100644
index 0000000..bc40b88
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/scheduler/ScheduledTask.java
@@ -0,0 +1,36 @@
+package net.md_5.bungee.api.scheduler;
+
+import net.md_5.bungee.api.plugin.Plugin;
+
+/**
+ * Represents a task scheduled for execution by the {@link TaskScheduler}.
+ */
+public interface ScheduledTask
+{
+
+ /**
+ * Gets the unique ID of this task.
+ *
+ * @return this tasks ID
+ */
+ int getId();
+
+ /**
+ * Return the plugin which scheduled this task for execution.
+ *
+ * @return the owning plugin
+ */
+ Plugin getOwner();
+
+ /**
+ * Get the actual method which will be executed by this task.
+ *
+ * @return the {@link Runnable} behind this task
+ */
+ Runnable getTask();
+
+ /**
+ * Cancel this task to suppress subsequent executions.
+ */
+ void cancel();
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/scheduler/TaskScheduler.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/scheduler/TaskScheduler.java
new file mode 100644
index 0000000..a5062eb
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/scheduler/TaskScheduler.java
@@ -0,0 +1,74 @@
+package net.md_5.bungee.api.scheduler;
+
+import java.util.concurrent.TimeUnit;
+import net.md_5.bungee.api.plugin.Plugin;
+
+/**
+ * This interface represents a scheduler which may be used to queue, delay and
+ * execute tasks in an asynchronous fashion.
+ */
+public interface TaskScheduler
+{
+
+ /**
+ * Cancel a task to prevent it from executing, or if its a repeating task,
+ * prevent its further execution.
+ *
+ * @param id the id of the task to cancel
+ */
+ void cancel(int id);
+
+ /**
+ * Cancel a task to prevent it from executing, or if its a repeating task,
+ * prevent its further execution.
+ *
+ * @param task the task to cancel
+ */
+ void cancel(ScheduledTask task);
+
+ /**
+ * Cancel all tasks owned by this plugin, this preventing them from being
+ * executed hereon in.
+ *
+ * @param plugin the plugin owning the tasks to be cancelled
+ * @return the number of tasks cancelled by this method
+ */
+ int cancel(Plugin plugin);
+
+ /**
+ * Schedule a task to be executed asynchronously. The task will commence
+ * running as soon as this method returns.
+ *
+ * @param owner the plugin owning this task
+ * @param task the task to run
+ * @return the scheduled task
+ */
+ ScheduledTask runAsync(Plugin owner, Runnable task);
+
+ /**
+ * Schedules a task to be executed asynchronously after the specified delay
+ * is up.
+ *
+ * @param owner the plugin owning this task
+ * @param task the task to run
+ * @param delay the delay before this task will be executed
+ * @param unit the unit in which the delay will be measured
+ * @return the scheduled task
+ */
+ ScheduledTask schedule(Plugin owner, Runnable task, long delay, TimeUnit unit);
+
+ /**
+ * Schedules a task to be executed asynchronously after the specified delay
+ * is up. The scheduled task will continue running at the specified
+ * interval. The interval will not begin to count down until the last task
+ * invocation is complete.
+ *
+ * @param owner the plugin owning this task
+ * @param task the task to run
+ * @param delay the delay in milliseconds before this task will be executed
+ * @param period the interval before subsequent executions of this task
+ * @param unit the unit in which the delay and period will be measured
+ * @return the scheduled task
+ */
+ ScheduledTask schedule(Plugin owner, Runnable task, long delay, long period, TimeUnit unit);
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/score/Objective.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/score/Objective.java
new file mode 100644
index 0000000..ec1d0e1
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/score/Objective.java
@@ -0,0 +1,89 @@
+package net.md_5.bungee.api.score;
+
+import java.beans.ConstructorProperties;
+
+/**
+ * Represents an objective entry.
+ */
+public class Objective
+{
+
+ /**
+ * Name of the objective.
+ */
+ private final String name;
+ /**
+ * Value of the objective.
+ */
+ private final String value; // displayName
+
+ @ConstructorProperties({ "name", "value" })
+ public Objective(final String name, final String value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public String getValue() {
+ return this.value;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof Objective)) {
+ return false;
+ }
+ final Objective other = (Objective) o;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ final Object this$name = this.getName();
+ final Object other$name = other.getName();
+ Label_0065: {
+ if (this$name == null) {
+ if (other$name == null) {
+ break Label_0065;
+ }
+ } else if (this$name.equals(other$name)) {
+ break Label_0065;
+ }
+ return false;
+ }
+ final Object this$value = this.getValue();
+ final Object other$value = other.getValue();
+ if (this$value == null) {
+ if (other$value == null) {
+ return true;
+ }
+ } else if (this$value.equals(other$value)) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean canEqual(final Object other) {
+ return other instanceof Objective;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ final Object $name = this.getName();
+ result = result * 31 + (($name == null) ? 0 : $name.hashCode());
+ final Object $value = this.getValue();
+ result = result * 31 + (($value == null) ? 0 : $value.hashCode());
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Objective(name=" + this.getName() + ", value=" + this.getValue() + ")";
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/score/Position.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/score/Position.java
new file mode 100644
index 0000000..e68d066
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/score/Position.java
@@ -0,0 +1,10 @@
+package net.md_5.bungee.api.score;
+
+/**
+ * Represents locations for a scoreboard to be displayed.
+ */
+public enum Position
+{
+
+ LIST, SIDEBAR, BELOW;
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/score/Score.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/score/Score.java
new file mode 100644
index 0000000..4384c33
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/score/Score.java
@@ -0,0 +1,99 @@
+package net.md_5.bungee.api.score;
+
+import java.beans.ConstructorProperties;
+
+/**
+ * Represents a scoreboard score entry.
+ */
+public class Score
+{
+
+ /**
+ * Name to be displayed in the list.
+ */
+ private final String itemName; // Player
+ /**
+ * Unique name of the score.
+ */
+ private final String scoreName; // Score
+ /**
+ * Value of the score.
+ */
+ private final int value;
+
+ @ConstructorProperties({ "itemName", "scoreName", "value" })
+ public Score(final String itemName, final String scoreName, final int value) {
+ this.itemName = itemName;
+ this.scoreName = scoreName;
+ this.value = value;
+ }
+
+ public String getItemName() {
+ return this.itemName;
+ }
+
+ public String getScoreName() {
+ return this.scoreName;
+ }
+
+ public int getValue() {
+ return this.value;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof Score)) {
+ return false;
+ }
+ final Score other = (Score) o;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ final Object this$itemName = this.getItemName();
+ final Object other$itemName = other.getItemName();
+ Label_0065: {
+ if (this$itemName == null) {
+ if (other$itemName == null) {
+ break Label_0065;
+ }
+ } else if (this$itemName.equals(other$itemName)) {
+ break Label_0065;
+ }
+ return false;
+ }
+ final Object this$scoreName = this.getScoreName();
+ final Object other$scoreName = other.getScoreName();
+ if (this$scoreName == null) {
+ if (other$scoreName == null) {
+ return this.getValue() == other.getValue();
+ }
+ } else if (this$scoreName.equals(other$scoreName)) {
+ return this.getValue() == other.getValue();
+ }
+ return false;
+ }
+
+ public boolean canEqual(final Object other) {
+ return other instanceof Score;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ final Object $itemName = this.getItemName();
+ result = result * 31 + (($itemName == null) ? 0 : $itemName.hashCode());
+ final Object $scoreName = this.getScoreName();
+ result = result * 31 + (($scoreName == null) ? 0 : $scoreName.hashCode());
+ result = result * 31 + this.getValue();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Score(itemName=" + this.getItemName() + ", scoreName=" + this.getScoreName() + ", value=" + this.getValue() + ")";
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/score/Scoreboard.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/score/Scoreboard.java
new file mode 100644
index 0000000..93791d1
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/score/Scoreboard.java
@@ -0,0 +1,193 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.api.score;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.common.base.Preconditions;
+
+public class Scoreboard {
+ private String name;
+ private Position position;
+ private final Map objectives;
+ private final Map scores;
+ private final Map teams;
+
+ public Collection getObjectives() {
+ return Collections.unmodifiableCollection((Collection extends Objective>) this.objectives.values());
+ }
+
+ public Collection getScores() {
+ return Collections.unmodifiableCollection((Collection extends Score>) this.scores.values());
+ }
+
+ public Collection getTeams() {
+ return Collections.unmodifiableCollection((Collection extends Team>) this.teams.values());
+ }
+
+ public void addObjective(final Objective objective) {
+ Preconditions.checkNotNull((Object) objective, (Object) "objective");
+ Preconditions.checkArgument(!this.objectives.containsKey(objective.getName()), "Objective %s already exists in this scoreboard", new Object[] { objective.getName() });
+ this.objectives.put(objective.getName(), objective);
+ }
+
+ public void addScore(final Score score) {
+ Preconditions.checkNotNull((Object) score, (Object) "score");
+ this.scores.put(score.getItemName(), score);
+ }
+
+ public void addTeam(final Team team) {
+ Preconditions.checkNotNull((Object) team, (Object) "team");
+ Preconditions.checkArgument(!this.teams.containsKey(team.getName()), "Team %s already exists in this scoreboard", new Object[] { team.getName() });
+ this.teams.put(team.getName(), team);
+ }
+
+ public Team getTeam(final String name) {
+ return this.teams.get(name);
+ }
+
+ public void removeObjective(final String objectiveName) {
+ this.objectives.remove(objectiveName);
+ }
+
+ public void removeScore(final String scoreName) {
+ this.scores.remove(scoreName);
+ }
+
+ public void removeTeam(final String teamName) {
+ this.teams.remove(teamName);
+ }
+
+ public void clear() {
+ this.name = null;
+ this.position = null;
+ this.objectives.clear();
+ this.scores.clear();
+ this.teams.clear();
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public Position getPosition() {
+ return this.position;
+ }
+
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ public void setPosition(final Position position) {
+ this.position = position;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof Scoreboard)) {
+ return false;
+ }
+ final Scoreboard other = (Scoreboard) o;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ final Object this$name = this.getName();
+ final Object other$name = other.getName();
+ Label_0065: {
+ if (this$name == null) {
+ if (other$name == null) {
+ break Label_0065;
+ }
+ } else if (this$name.equals(other$name)) {
+ break Label_0065;
+ }
+ return false;
+ }
+ final Object this$position = this.getPosition();
+ final Object other$position = other.getPosition();
+ Label_0102: {
+ if (this$position == null) {
+ if (other$position == null) {
+ break Label_0102;
+ }
+ } else if (this$position.equals(other$position)) {
+ break Label_0102;
+ }
+ return false;
+ }
+ final Object this$objectives = this.getObjectives();
+ final Object other$objectives = other.getObjectives();
+ Label_0139: {
+ if (this$objectives == null) {
+ if (other$objectives == null) {
+ break Label_0139;
+ }
+ } else if (this$objectives.equals(other$objectives)) {
+ break Label_0139;
+ }
+ return false;
+ }
+ final Object this$scores = this.getScores();
+ final Object other$scores = other.getScores();
+ Label_0176: {
+ if (this$scores == null) {
+ if (other$scores == null) {
+ break Label_0176;
+ }
+ } else if (this$scores.equals(other$scores)) {
+ break Label_0176;
+ }
+ return false;
+ }
+ final Object this$teams = this.getTeams();
+ final Object other$teams = other.getTeams();
+ if (this$teams == null) {
+ if (other$teams == null) {
+ return true;
+ }
+ } else if (this$teams.equals(other$teams)) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean canEqual(final Object other) {
+ return other instanceof Scoreboard;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ final Object $name = this.getName();
+ result = result * 31 + (($name == null) ? 0 : $name.hashCode());
+ final Object $position = this.getPosition();
+ result = result * 31 + (($position == null) ? 0 : $position.hashCode());
+ final Object $objectives = this.getObjectives();
+ result = result * 31 + (($objectives == null) ? 0 : $objectives.hashCode());
+ final Object $scores = this.getScores();
+ result = result * 31 + (($scores == null) ? 0 : $scores.hashCode());
+ final Object $teams = this.getTeams();
+ result = result * 31 + (($teams == null) ? 0 : $teams.hashCode());
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Scoreboard(name=" + this.getName() + ", position=" + this.getPosition() + ", objectives=" + this.getObjectives() + ", scores=" + this.getScores() + ", teams=" + this.getTeams() + ")";
+ }
+
+ public Scoreboard() {
+ this.objectives = new HashMap();
+ this.scores = new HashMap();
+ this.teams = new HashMap();
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/score/Team.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/score/Team.java
new file mode 100644
index 0000000..5072b0e
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/score/Team.java
@@ -0,0 +1,183 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.api.score;
+
+import java.beans.ConstructorProperties;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+public class Team {
+ private final String name;
+ private String displayName;
+ private String prefix;
+ private String suffix;
+ private boolean friendlyFire;
+ private Set players;
+
+ public Collection getPlayers() {
+ return (Collection) (Collection>) Collections.unmodifiableSet((Set>) this.players);
+ }
+
+ public void addPlayer(final String name) {
+ this.players.add(name);
+ }
+
+ public void removePlayer(final String name) {
+ this.players.remove(name);
+ }
+
+ @ConstructorProperties({ "name" })
+ public Team(final String name) {
+ this.players = new HashSet();
+ if (name == null) {
+ throw new NullPointerException("name");
+ }
+ this.name = name;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public String getDisplayName() {
+ return this.displayName;
+ }
+
+ public String getPrefix() {
+ return this.prefix;
+ }
+
+ public String getSuffix() {
+ return this.suffix;
+ }
+
+ public boolean isFriendlyFire() {
+ return this.friendlyFire;
+ }
+
+ public void setDisplayName(final String displayName) {
+ this.displayName = displayName;
+ }
+
+ public void setPrefix(final String prefix) {
+ this.prefix = prefix;
+ }
+
+ public void setSuffix(final String suffix) {
+ this.suffix = suffix;
+ }
+
+ public void setFriendlyFire(final boolean friendlyFire) {
+ this.friendlyFire = friendlyFire;
+ }
+
+ public void setPlayers(final Set players) {
+ this.players = players;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof Team)) {
+ return false;
+ }
+ final Team other = (Team) o;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ final Object this$name = this.getName();
+ final Object other$name = other.getName();
+ Label_0065: {
+ if (this$name == null) {
+ if (other$name == null) {
+ break Label_0065;
+ }
+ } else if (this$name.equals(other$name)) {
+ break Label_0065;
+ }
+ return false;
+ }
+ final Object this$displayName = this.getDisplayName();
+ final Object other$displayName = other.getDisplayName();
+ Label_0102: {
+ if (this$displayName == null) {
+ if (other$displayName == null) {
+ break Label_0102;
+ }
+ } else if (this$displayName.equals(other$displayName)) {
+ break Label_0102;
+ }
+ return false;
+ }
+ final Object this$prefix = this.getPrefix();
+ final Object other$prefix = other.getPrefix();
+ Label_0139: {
+ if (this$prefix == null) {
+ if (other$prefix == null) {
+ break Label_0139;
+ }
+ } else if (this$prefix.equals(other$prefix)) {
+ break Label_0139;
+ }
+ return false;
+ }
+ final Object this$suffix = this.getSuffix();
+ final Object other$suffix = other.getSuffix();
+ Label_0176: {
+ if (this$suffix == null) {
+ if (other$suffix == null) {
+ break Label_0176;
+ }
+ } else if (this$suffix.equals(other$suffix)) {
+ break Label_0176;
+ }
+ return false;
+ }
+ if (this.isFriendlyFire() != other.isFriendlyFire()) {
+ return false;
+ }
+ final Object this$players = this.getPlayers();
+ final Object other$players = other.getPlayers();
+ if (this$players == null) {
+ if (other$players == null) {
+ return true;
+ }
+ } else if (this$players.equals(other$players)) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean canEqual(final Object other) {
+ return other instanceof Team;
+ }
+
+ @Override
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ final Object $name = this.getName();
+ result = result * 31 + (($name == null) ? 0 : $name.hashCode());
+ final Object $displayName = this.getDisplayName();
+ result = result * 31 + (($displayName == null) ? 0 : $displayName.hashCode());
+ final Object $prefix = this.getPrefix();
+ result = result * 31 + (($prefix == null) ? 0 : $prefix.hashCode());
+ final Object $suffix = this.getSuffix();
+ result = result * 31 + (($suffix == null) ? 0 : $suffix.hashCode());
+ result = result * 31 + (this.isFriendlyFire() ? 1231 : 1237);
+ final Object $players = this.getPlayers();
+ result = result * 31 + (($players == null) ? 0 : $players.hashCode());
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Team(name=" + this.getName() + ", displayName=" + this.getDisplayName() + ", prefix=" + this.getPrefix() + ", suffix=" + this.getSuffix() + ", friendlyFire=" + this.isFriendlyFire() + ", players=" + this.getPlayers() + ")";
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/tab/CustomTabList.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/tab/CustomTabList.java
new file mode 100644
index 0000000..09aaa7f
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/tab/CustomTabList.java
@@ -0,0 +1,60 @@
+package net.md_5.bungee.api.tab;
+
+/**
+ * Represents a custom tab list, which may have slots manipulated.
+ */
+public interface CustomTabList extends TabListHandler
+{
+
+ /**
+ * Blank out this tab list and update immediately.
+ */
+ void clear();
+
+ /**
+ * Gets the columns in this list.
+ *
+ * @return the width of this list
+ */
+ int getColumns();
+
+ /**
+ * Gets the rows in this list.
+ *
+ * @return the height of this list
+ */
+ int getRows();
+
+ /**
+ * Get the total size of this list.
+ *
+ * @return {@link #getRows()} * {@link #getColumns()}
+ */
+ int getSize();
+
+ /**
+ * Set the text in the specified slot and update immediately.
+ *
+ * @param row the row to set
+ * @param column the column to set
+ * @param text the text to set
+ * @return the padded text
+ */
+ String setSlot(int row, int column, String text);
+
+ /**
+ * Set the text in the specified slot.
+ *
+ * @param row the row to set
+ * @param column the column to set
+ * @param text the text to set
+ * @param update whether or not to invoke {@link #update()} upon completion
+ * @return the padded text
+ */
+ String setSlot(int row, int column, String text, boolean update);
+
+ /**
+ * Flush all queued changes to the user.
+ */
+ void update();
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/tab/TabListAdapter.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/tab/TabListAdapter.java
new file mode 100644
index 0000000..c344010
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/tab/TabListAdapter.java
@@ -0,0 +1,36 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.api.tab;
+
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+
+public abstract class TabListAdapter implements TabListHandler {
+ private ProxiedPlayer player;
+
+ @Override
+ public void init(final ProxiedPlayer player) {
+ this.player = player;
+ }
+
+ @Override
+ public void onConnect() {
+ }
+
+ @Override
+ public void onDisconnect() {
+ }
+
+ @Override
+ public void onServerChange() {
+ }
+
+ @Override
+ public void onPingChange(final int ping) {
+ }
+
+ public ProxiedPlayer getPlayer() {
+ return this.player;
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/api/tab/TabListHandler.java b/eaglerbungee/src/main/java/net/md_5/bungee/api/tab/TabListHandler.java
new file mode 100644
index 0000000..e47d4ad
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/api/tab/TabListHandler.java
@@ -0,0 +1,55 @@
+package net.md_5.bungee.api.tab;
+
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+
+public interface TabListHandler
+{
+
+ /**
+ * Called so that this class may set member fields to keep track of its
+ * internal state. You should not do any packet sending or manipulation of
+ * the passed player, other than storing it.
+ *
+ * @param player the player to be associated with this list
+ */
+ void init(ProxiedPlayer player);
+
+ /**
+ * Called when this player first connects to the proxy.
+ */
+ void onConnect();
+
+ /**
+ * Called when a player first connects to the proxy.
+ *
+ * @param player the connecting player
+ */
+ void onServerChange();
+
+ /**
+ * Called when a players ping changes. The new ping will have not updated in
+ * the player instance until this method returns.
+ *
+ * @param player the player who's ping changed
+ * @param ping the player's new ping.
+ */
+ void onPingChange(int ping);
+
+ /**
+ * Called when a player disconnects.
+ *
+ * @param player the disconnected player
+ */
+ void onDisconnect();
+
+ /**
+ * Called when a list update packet is sent from server to client.
+ *
+ * @param player receiving this packet
+ * @param name the player which this packet is relevant to
+ * @param online whether the subject player is online
+ * @param ping ping of the subject player
+ * @return whether to send the packet to the client
+ */
+ boolean onListUpdate(String name, boolean online, int ping);
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandAlert.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandAlert.java
new file mode 100644
index 0000000..1ccae73
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandAlert.java
@@ -0,0 +1,46 @@
+package net.md_5.bungee.command;
+
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.plugin.Command;
+
+public class CommandAlert extends Command
+{
+
+ public CommandAlert()
+ {
+ super( "alert", "bungeecord.command.alert" );
+ }
+
+ @Override
+ public void execute(CommandSender sender, String[] args)
+ {
+ if ( args.length == 0 )
+ {
+ sender.sendMessage( ChatColor.RED + "You must supply a message." );
+ } else
+ {
+ StringBuilder builder = new StringBuilder();
+ if ( args[0].startsWith( "&h" ) )
+ {
+ // Remove &h
+ args[0] = args[0].substring( 2, args[0].length() );
+ } else
+ {
+ builder.append( ProxyServer.getInstance().getTranslation( "alert" ) );
+ }
+
+ for ( String s : args )
+ {
+ builder.append( ChatColor.translateAlternateColorCodes( '&', s ) );
+ builder.append( " " );
+ }
+
+ String message = builder.substring( 0, builder.length() - 1 );
+
+ ProxyServer.getInstance().broadcast( message );
+ }
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandBungee.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandBungee.java
new file mode 100644
index 0000000..b079879
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandBungee.java
@@ -0,0 +1,21 @@
+package net.md_5.bungee.command;
+
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.plugin.Command;
+
+public class CommandBungee extends Command
+{
+
+ public CommandBungee()
+ {
+ super( "bungee" );
+ }
+
+ @Override
+ public void execute(CommandSender sender, String[] args)
+ {
+ sender.sendMessage( ChatColor.BLUE + "This server is running BungeeCord version " + ProxyServer.getInstance().getVersion() + " by md_5" );
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandChangePassword.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandChangePassword.java
new file mode 100644
index 0000000..1a29b2d
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandChangePassword.java
@@ -0,0 +1,35 @@
+package net.md_5.bungee.command;
+
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.plugin.Command;
+import net.md_5.bungee.eaglercraft.AuthSystem;
+
+public class CommandChangePassword extends Command {
+ private final AuthSystem authSystem;
+
+ public CommandChangePassword(AuthSystem authSystem) {
+ super("changepassword", "bungeecord.command.eag.changepassword",
+ new String[] { "changepwd", "changepasswd", "changepass" });
+ this.authSystem = authSystem;
+ }
+
+ @Override
+ public void execute(final CommandSender sender, final String[] args) {
+ if (!(sender instanceof ProxiedPlayer)) {
+ return;
+ }
+ String username = sender.getName();
+ if (args.length == 0 || args.length == 1) {
+ sender.sendMessage("\u00A7cUsage: /changepassword ");
+ } else if (this.authSystem.login(username, args[0])) {
+ if (this.authSystem.changePass(username, args[1])) {
+ sender.sendMessage("\u00A7cPassword changed successfully!");
+ } else {
+ sender.sendMessage("\u00A7cUnable to change your password...");
+ }
+ } else {
+ sender.sendMessage("\u00A7cThe old password specified is incorrect!");
+ }
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandClearRatelimit.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandClearRatelimit.java
new file mode 100644
index 0000000..12440d1
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandClearRatelimit.java
@@ -0,0 +1,118 @@
+package net.md_5.bungee.command;
+
+import net.md_5.bungee.BungeeCord;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.config.ListenerInfo;
+import net.md_5.bungee.api.plugin.Command;
+
+public class CommandClearRatelimit extends Command {
+
+ public CommandClearRatelimit() {
+ super("eag-ratelimit", "bungeecord.command.eag.ratelimit", "e-ratelimit", "gratelimit");
+ }
+
+ @Override
+ public void execute(CommandSender p0, String[] p1) {
+ if(p1.length >= 1 && ("clear".equalsIgnoreCase(p1[0]) || "reset".equalsIgnoreCase(p1[0]))) {
+ if(p1.length == 1 || (p1.length == 2 && "all".equalsIgnoreCase(p1[1]))) {
+ for(ListenerInfo l : BungeeCord.getInstance().config.getListeners()) {
+ if(l.getRateLimitIP() != null) l.getRateLimitIP().resetLimiters();
+ if(l.getRateLimitLogin() != null) l.getRateLimitLogin().resetLimiters();
+ if(l.getRateLimitMOTD() != null) l.getRateLimitMOTD().resetLimiters();
+ if(l.getRateLimitQuery() != null) l.getRateLimitQuery().resetLimiters();
+ }
+ p0.sendMessage(ChatColor.GREEN + "Reset all ratelimits");
+ return;
+ }else if(p1.length == 2 || p1.length == 3) {
+ ListenerInfo ll = null;
+ if(p1.length == 3) {
+ for(ListenerInfo l : BungeeCord.getInstance().config.getListeners()) {
+ if(l.getHostString().equalsIgnoreCase(p1[2])) {
+ ll = l;
+ break;
+ }
+ }
+ if(ll == null) {
+ p0.sendMessage(ChatColor.RED + "Listener does not exist: " + ChatColor.WHITE + p1[2]);
+ String accum = "";
+ for(ListenerInfo l : BungeeCord.getInstance().config.getListeners()) {
+ if(accum.length() > 0) {
+ accum += ", ";
+ }
+ accum += l.getHostString();
+ }
+ p0.sendMessage(ChatColor.GREEN + "Listeners Available: " + ChatColor.WHITE + accum);
+ return;
+ }
+ }
+ if("all".equalsIgnoreCase(p1[1])) {
+ if(ll != null) {
+ if(ll.getRateLimitIP() != null) ll.getRateLimitIP().resetLimiters();
+ if(ll.getRateLimitLogin() != null) ll.getRateLimitLogin().resetLimiters();
+ if(ll.getRateLimitMOTD() != null) ll.getRateLimitMOTD().resetLimiters();
+ if(ll.getRateLimitQuery() != null) ll.getRateLimitQuery().resetLimiters();
+ p0.sendMessage(ChatColor.GREEN + "Reset all ratelimits on listener: " + ChatColor.WHITE + ll.getHostString());
+ }else {
+ for(ListenerInfo l : BungeeCord.getInstance().config.getListeners()) {
+ if(l.getRateLimitIP() != null) l.getRateLimitIP().resetLimiters();
+ if(l.getRateLimitLogin() != null) l.getRateLimitLogin().resetLimiters();
+ if(l.getRateLimitMOTD() != null) l.getRateLimitMOTD().resetLimiters();
+ if(l.getRateLimitQuery() != null) l.getRateLimitQuery().resetLimiters();
+ }
+ p0.sendMessage(ChatColor.GREEN + "Reset all ratelimits");
+ }
+ return;
+ }else if("ip".equalsIgnoreCase(p1[1])) {
+ if(ll != null) {
+ if(ll.getRateLimitIP() != null) ll.getRateLimitIP().resetLimiters();
+ p0.sendMessage(ChatColor.GREEN + "Reset all IP ratelimits on listener: " + ChatColor.WHITE + ll.getHostString());
+ }else {
+ for(ListenerInfo l : BungeeCord.getInstance().config.getListeners()) {
+ if(l.getRateLimitIP() != null) l.getRateLimitIP().resetLimiters();
+ }
+ p0.sendMessage(ChatColor.GREEN + "Reset all IP ratelimits.");
+ }
+ return;
+ }else if("login".equalsIgnoreCase(p1[1])) {
+ if(ll != null) {
+ if(ll.getRateLimitLogin() != null) ll.getRateLimitLogin().resetLimiters();
+ p0.sendMessage(ChatColor.GREEN + "Reset all login ratelimits on listener: " + ChatColor.WHITE + ll.getHostString());
+ }else {
+ for(ListenerInfo l : BungeeCord.getInstance().config.getListeners()) {
+ if(l.getRateLimitLogin() != null) l.getRateLimitLogin().resetLimiters();
+ }
+ p0.sendMessage(ChatColor.GREEN + "Reset all login ratelimits.");
+ }
+ return;
+ }else if("motd".equalsIgnoreCase(p1[1])) {
+ if(ll != null) {
+ if(ll.getRateLimitMOTD() != null) ll.getRateLimitMOTD().resetLimiters();
+ p0.sendMessage(ChatColor.GREEN + "Reset all MOTD ratelimits on listener: " + ChatColor.WHITE + ll.getHostString());
+ }else {
+ for(ListenerInfo l : BungeeCord.getInstance().config.getListeners()) {
+ if(l.getRateLimitMOTD() != null) l.getRateLimitMOTD().resetLimiters();
+ }
+ p0.sendMessage(ChatColor.GREEN + "Reset all MOTD ratelimits.");
+ }
+ return;
+ }else if("query".equalsIgnoreCase(p1[1])) {
+ if(ll != null) {
+ if(ll.getRateLimitMOTD() != null) ll.getRateLimitMOTD().resetLimiters();
+ p0.sendMessage(ChatColor.GREEN + "Reset all query ratelimits on listener: " + ChatColor.WHITE + ll.getHostString());
+ }else {
+ for(ListenerInfo l : BungeeCord.getInstance().config.getListeners()) {
+ if(l.getRateLimitMOTD() != null) l.getRateLimitMOTD().resetLimiters();
+ }
+ p0.sendMessage(ChatColor.GREEN + "Reset all query ratelimits.");
+ }
+ return;
+ }
+ }
+ }
+ p0.sendMessage(ChatColor.RED + "How to reset all rate limits: " + ChatColor.WHITE + "/eag-ratelimit reset");
+ p0.sendMessage(ChatColor.RED + "How to reset a specific rate limit: " + ChatColor.WHITE + "/eag-ratelimit reset ");
+ p0.sendMessage(ChatColor.RED + "How to reset a specific listener: " + ChatColor.WHITE + "/eag-ratelimit reset ");
+ }
+
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandConfirmCode.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandConfirmCode.java
new file mode 100644
index 0000000..d5bc09d
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandConfirmCode.java
@@ -0,0 +1,33 @@
+package net.md_5.bungee.command;
+
+import java.nio.charset.StandardCharsets;
+
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.plugin.Command;
+import net.md_5.bungee.eaglercraft.QueryConnectionImpl;
+import net.md_5.bungee.eaglercraft.SHA1Digest;
+
+public class CommandConfirmCode extends Command {
+
+ public CommandConfirmCode() {
+ super("confirm-code", "bungeecord.command.eag.confirmcode", "confirmcode");
+ }
+
+ @Override
+ public void execute(CommandSender p0, String[] p1) {
+ if(p1.length != 1) {
+ p0.sendMessage(ChatColor.RED + "How to use: " + ChatColor.WHITE + "/confirm-code ");
+ }else {
+ p0.sendMessage(ChatColor.YELLOW + "Server list 2FA code has been set to: " + ChatColor.GREEN + p1[0]);
+ p0.sendMessage(ChatColor.YELLOW + "You can now return to the server list site and continue");
+ byte[] bts = p1[0].getBytes(StandardCharsets.US_ASCII);
+ SHA1Digest dg = new SHA1Digest();
+ dg.update(bts, 0, bts.length);
+ byte[] f = new byte[20];
+ dg.doFinal(f, 0);
+ QueryConnectionImpl.confirmHash = SHA1Digest.hash2string(f);
+ }
+ }
+
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandDomain.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandDomain.java
new file mode 100644
index 0000000..fbb985b
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandDomain.java
@@ -0,0 +1,37 @@
+package net.md_5.bungee.command;
+
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.plugin.Command;
+
+public class CommandDomain extends Command {
+
+ public CommandDomain() {
+ super("domain", "bungeecord.command.eag.domain");
+ }
+
+ @Override
+ public void execute(CommandSender p0, String[] p1) {
+ if (p1.length < 1) {
+ p0.sendMessage(ChatColor.RED + "Please follow this command by a user name");
+ return;
+ }
+ final ProxiedPlayer user = ProxyServer.getInstance().getPlayer(p1[0]);
+ if (user == null) {
+ p0.sendMessage(ChatColor.RED + "That user is not online");
+ } else {
+ Object o = user.getAttachment().get("origin");
+ if(o != null) {
+ p0.sendMessage(ChatColor.BLUE + "Domain of " + p1[0] + " is " + o);
+ if(p0.hasPermission("bungeecord.command.eag.blockdomain")) {
+ p0.sendMessage(ChatColor.BLUE + "Type " + ChatColor.WHITE + "/block-domain " + p1[0] + ChatColor.BLUE + " to block this person");
+ }
+ }else {
+ p0.sendMessage(ChatColor.RED + "Domain of " + p1[0] + " is unknown");
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandDomainBlock.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandDomainBlock.java
new file mode 100644
index 0000000..983b8e6
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandDomainBlock.java
@@ -0,0 +1,38 @@
+package net.md_5.bungee.command;
+
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.plugin.Command;
+import net.md_5.bungee.eaglercraft.DomainBlacklist;
+
+public class CommandDomainBlock extends Command {
+
+ public CommandDomainBlock() {
+ super("block-domain", "bungeecord.command.eag.blockdomain");
+ }
+
+ @Override
+ public void execute(CommandSender p0, String[] p1) {
+ if (p1.length < 1) {
+ p0.sendMessage(ChatColor.RED + "Please follow this command by a username");
+ return;
+ }
+ final ProxiedPlayer user = ProxyServer.getInstance().getPlayer(p1[0]);
+ if (user == null) {
+ p0.sendMessage(ChatColor.RED + "That user is not online");
+ }else {
+ Object o = user.getAttachment().get("origin");
+ if(o != null) {
+ DomainBlacklist.addLocal((String)o);
+ p0.sendMessage(ChatColor.RED + "Domain of " + ChatColor.WHITE + p1[0] + ChatColor.RED + " is " + ChatColor.WHITE + o);
+ p0.sendMessage(ChatColor.RED + "It was added to the local block list.");
+ user.disconnect("client blocked");
+ }else {
+ p0.sendMessage(ChatColor.RED + "Domain of " + p1[0] + " is unknown");
+ }
+ }
+ }
+
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandDomainBlockDomain.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandDomainBlockDomain.java
new file mode 100644
index 0000000..8533de8
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandDomainBlockDomain.java
@@ -0,0 +1,24 @@
+package net.md_5.bungee.command;
+
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.plugin.Command;
+import net.md_5.bungee.eaglercraft.DomainBlacklist;
+
+public class CommandDomainBlockDomain extends Command {
+
+ public CommandDomainBlockDomain() {
+ super("block-domain-name", "bungeecord.command.eag.blockdomainname");
+ }
+
+ @Override
+ public void execute(CommandSender p0, String[] p1) {
+ if (p1.length < 1) {
+ p0.sendMessage(ChatColor.RED + "Please follow this command by a domain");
+ return;
+ }
+ DomainBlacklist.addLocal(p1[0]);
+ p0.sendMessage(ChatColor.GREEN + "The domain '" + ChatColor.WHITE + p1[0] + ChatColor.GREEN + "' was added to the block list");
+ }
+
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandDomainUnblock.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandDomainUnblock.java
new file mode 100644
index 0000000..2a0ab1b
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandDomainUnblock.java
@@ -0,0 +1,27 @@
+package net.md_5.bungee.command;
+
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.plugin.Command;
+import net.md_5.bungee.eaglercraft.DomainBlacklist;
+
+public class CommandDomainUnblock extends Command {
+
+ public CommandDomainUnblock() {
+ super("unblock-domain", "bungeecord.command.eag.unblockdomain", "unblock-domain-name");
+ }
+
+ @Override
+ public void execute(CommandSender p0, String[] p1) {
+ if (p1.length < 1) {
+ p0.sendMessage(ChatColor.RED + "Please follow this command by a domain");
+ return;
+ }
+ if(DomainBlacklist.removeLocal(p1[0])) {
+ p0.sendMessage(ChatColor.GREEN + "The domain '" + p1[0] + "' was removed from the local block list");
+ }else {
+ p0.sendMessage(ChatColor.RED + "The domain was not removed, is it on the block list? Check '" + DomainBlacklist.localBlacklist.getName() + "' in your bungeecord directory");
+ }
+ }
+
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandEnd.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandEnd.java
new file mode 100644
index 0000000..e8fd283
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandEnd.java
@@ -0,0 +1,23 @@
+package net.md_5.bungee.command;
+
+import net.md_5.bungee.BungeeCord;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.plugin.Command;
+
+/**
+ * Command to terminate the proxy instance. May only be used by the console.
+ */
+public class CommandEnd extends Command
+{
+
+ public CommandEnd()
+ {
+ super( "end", "bungeecord.command.end" );
+ }
+
+ @Override
+ public void execute(CommandSender sender, String[] args)
+ {
+ BungeeCord.getInstance().stop();
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandFind.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandFind.java
new file mode 100644
index 0000000..10e99d7
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandFind.java
@@ -0,0 +1,35 @@
+package net.md_5.bungee.command;
+
+import java.util.Collections;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+
+public class CommandFind extends PlayerCommand
+{
+
+ public CommandFind()
+ {
+ super( "find", "bungeecord.command.find" );
+ }
+
+ @Override
+ public void execute(CommandSender sender, String[] args)
+ {
+ if ( args.length != 1 )
+ {
+ sender.sendMessage( ChatColor.RED + "Please follow this command by a user name" );
+ } else
+ {
+ ProxiedPlayer player = ProxyServer.getInstance().getPlayer( args[0] );
+ if ( player == null || player.getServer() == null )
+ {
+ sender.sendMessage( ChatColor.RED + "That user is not online" );
+ } else
+ {
+ sender.sendMessage( ChatColor.BLUE + args[0] + " is online at " + player.getServer().getInfo().getName() );
+ }
+ }
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBan.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBan.java
new file mode 100644
index 0000000..2ee3dab
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBan.java
@@ -0,0 +1,61 @@
+package net.md_5.bungee.command;
+
+import java.util.Collection;
+
+import net.md_5.bungee.BungeeCord;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.plugin.Command;
+import net.md_5.bungee.eaglercraft.BanList;
+
+public class CommandGlobalBan extends Command {
+
+ private final boolean replaceBukkit;
+
+ public CommandGlobalBan(boolean replaceBukkit) {
+ super(replaceBukkit ? "ban" : "eag-ban", "bungeecord.command.eag.ban", replaceBukkit ? new String[] { "kickban", "eag-ban", "e-ban", "gban" } : new String[] { "e-ban", "gban" });
+ this.replaceBukkit = replaceBukkit;
+ }
+
+ @Override
+ public void execute(CommandSender p0, String[] p1) {
+ if(p1.length >= 1) {
+ String p = p1[0].trim().toLowerCase();
+ if(p0.getName().equalsIgnoreCase(p)) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "You cannot ban yourself");
+ return;
+ }
+ String reason = "The ban hammer has spoken!";
+ if(p1.length >= 2) {
+ reason = "";
+ for(int i = 1; i < p1.length; ++i) {
+ if(reason.length() > 0) {
+ reason += " ";
+ }
+ reason += p1[i];
+ }
+ }
+ String wasTheKick = null;
+ Collection playerz = BungeeCord.getInstance().getPlayers();
+ for(ProxiedPlayer pp : playerz) {
+ if(pp.getName().equalsIgnoreCase(p)) {
+ wasTheKick = pp.getName();
+ pp.disconnect("" + ChatColor.RED + "You are banned.\n" + ChatColor.DARK_GRAY + "Reason: " + reason);
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.WHITE + "Kicked: " + pp.getName());
+ }
+ }
+ if(BanList.ban(p, reason)) {
+ if(wasTheKick == null) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.YELLOW + "Warning! '" + ChatColor.WHITE + p + ChatColor.YELLOW + "' is not currently on this server");
+ }
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Username '" + ChatColor.WHITE + (wasTheKick == null ? p : wasTheKick) + ChatColor.GREEN + "' was added to the ban list");
+ }else {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Username '" + ChatColor.WHITE + p + ChatColor.RED + "' is already banned");
+ }
+ }else {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "To ban a player, use: " + ChatColor.WHITE + "/" + (replaceBukkit?"":"eag-") + "ban [reason]");
+ }
+ }
+
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanIP.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanIP.java
new file mode 100644
index 0000000..3f2d323
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanIP.java
@@ -0,0 +1,147 @@
+package net.md_5.bungee.command;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.md_5.bungee.BungeeCord;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.plugin.Command;
+import net.md_5.bungee.eaglercraft.BanList;
+import net.md_5.bungee.eaglercraft.BanList.IPBan;
+
+public class CommandGlobalBanIP extends Command {
+
+ private final boolean replaceBukkit;
+
+ public CommandGlobalBanIP(boolean replaceBukkit) {
+ super(replaceBukkit ? "ban-ip" : "eag-ban-ip", "bungeecord.command.eag.banip", (replaceBukkit ? new String[] {"eag-ban-ip", "banip", "e-ban-ip", "gban-ip"} :
+ new String[] {"gban-ip", "e-ban-ip", "gbanip", "e-banip"}) );
+ this.replaceBukkit = replaceBukkit;
+ }
+
+ @Override
+ public void execute(CommandSender p0, String[] p1) {
+ String w = (String) p0.getAttachment().get("banIPWaitingToAdd");
+ if(w != null) {
+ List lst = (List)p0.getAttachment().get("banIPWaitingToKick");
+ if(p1.length != 1 || (!p1[0].equalsIgnoreCase("confirm") && !p1[0].equalsIgnoreCase("cancel"))) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Type " + ChatColor.WHITE + (replaceBukkit ? "/ban-ip" : "/eag-ban-ip") + " confirm" + ChatColor.RED + " to add IP " + ChatColor.WHITE + w +
+ ChatColor.RED + " and ban " + ChatColor.WHITE + lst.size() + ChatColor.RED + " players");
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Type " + ChatColor.WHITE + (replaceBukkit ? "/ban-ip" : "/eag-ban-ip") + " cancel" + ChatColor.RED + " to cancel this operation");
+ }else {
+ if(p1[0].equalsIgnoreCase("confirm")) {
+ try {
+ if(BanList.banIP(w)) {
+ for(ProxiedPlayer pp : lst) {
+ pp.disconnect("" + ChatColor.RED + "You are banned.\n" + ChatColor.DARK_GRAY + "Reason: banned by IP");
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Kicked: " + ChatColor.WHITE + pp.getName());
+ }
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Added IP '" + ChatColor.WHITE + w + ChatColor.GREEN + "' to the ban list");
+ }else {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "IP '" + ChatColor.WHITE + w + ChatColor.RED + "' is already on the ban list");
+ }
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "ERROR: address '" + ChatColor.WHITE + w + ChatColor.RED + "' is suddenly invalid for some reason");
+ }
+ }else {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Canceled ban");
+ }
+ p0.getAttachment().remove("banIPWaitingToAdd");
+ p0.getAttachment().remove("banIPWaitingToKick");
+ }
+ return;
+ }
+ if(p1.length != 1) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "How to use: " + ChatColor.WHITE + (replaceBukkit ? "/ban-ip" : "/eag-ban-ip") + " ");
+ return;
+ }
+ boolean isPlayer = false;
+ IPBan p = null;
+ try {
+ p = BanList.constructIpBan(p1[0]);
+ }catch(Throwable t) {
+ for(ProxiedPlayer pp : BungeeCord.getInstance().getPlayers()) {
+ if(pp.getName().equalsIgnoreCase(p1[0])) {
+ Object addr = pp.getAttachment().get("remoteAddr");
+ if(addr != null) {
+ String newAddr = ((InetAddress)addr).getHostAddress();
+ isPlayer = true;
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Player '" + ChatColor.WHITE + p1[0] + ChatColor.GREEN + "' has IP " + ChatColor.WHITE + newAddr);
+ p1[0] = newAddr;
+ try {
+ p = BanList.constructIpBan(p1[0]);
+ }catch(UnknownHostException ex) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Address '" + ChatColor.WHITE + p1[0] + "' is suddenly invalid: " + ChatColor.WHITE + p1[0]);
+ return;
+ }
+ }
+ break;
+ }
+ }
+ if(!isPlayer) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Player '" + ChatColor.WHITE + p1[0] + "' is not on this server");
+ return;
+ }
+ }
+ boolean blocked = false;
+ for(IPBan b : BanList.blockedBans) {
+ if(b.checkBan(p.getBaseAddress()) || p.checkBan(b.getBaseAddress())) {
+ blocked = true;
+ }
+ }
+ if(blocked) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Cannot ban '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "', it will ban local addresses that may break your game");
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "To force, add to the " + ChatColor.WHITE + "[IPs]" + ChatColor.RED + " section of " + ChatColor.WHITE + "bans.txt" + ChatColor.RED + " in your bungee directory");
+ return;
+ }
+ boolean isSenderGonnaGetKicked = false;
+ List usersThatAreGonnaBeKicked = new ArrayList();
+ for(ProxiedPlayer pp : BungeeCord.getInstance().getPlayers()) {
+ Object addr = pp.getAttachment().get("remoteAddr");
+ if(addr != null) {
+ InetAddress addrr = (InetAddress)addr;
+ if(p.checkBan(addrr)) {
+ usersThatAreGonnaBeKicked.add(pp);
+ if(pp.getName().equalsIgnoreCase(p0.getName())) {
+ isSenderGonnaGetKicked = true;
+ break;
+ }
+ }
+ }
+ }
+ if(isSenderGonnaGetKicked) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "banning address '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' will ban you off of your own server");
+ return;
+ }
+ if(usersThatAreGonnaBeKicked.size() > 1) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "WARNING: banning address '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is gonna ban " +
+ ChatColor.WHITE + usersThatAreGonnaBeKicked.size() + ChatColor.RED + " players off of your server");
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Type " + ChatColor.WHITE + (replaceBukkit ? "/ban-ip" : "/eag-ban-ip") + " confirm" + ChatColor.RED + " to continue, or type " +
+ ChatColor.WHITE + (replaceBukkit ? "/ban-ip" : "/eag-ban-ip") + " cancel" + ChatColor.RED + " to cancel");
+ p0.getAttachment().put("banIPWaitingToKick", usersThatAreGonnaBeKicked);
+ p0.getAttachment().put("banIPWaitingToAdd", p1[0]);
+ }else {
+ try {
+ if(BanList.banIP(p1[0])) {
+ if(usersThatAreGonnaBeKicked.size() > 0) {
+ usersThatAreGonnaBeKicked.get(0).disconnect("" + ChatColor.RED + "You are banned.\n" + ChatColor.DARK_GRAY + "Reason: banned by IP");
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Kicked: " + ChatColor.WHITE + usersThatAreGonnaBeKicked.get(0).getName());
+ }
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Added IP '" + ChatColor.WHITE + p1[0] + ChatColor.GREEN + "' to the ban list");
+ }else {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "IP '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is already on the ban list");
+ }
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "ERROR: address '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is suddenly invalid for some reason");
+ return;
+ }
+ }
+ }
+
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanRegex.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanRegex.java
new file mode 100644
index 0000000..076711f
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanRegex.java
@@ -0,0 +1,106 @@
+package net.md_5.bungee.command;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import net.md_5.bungee.BungeeCord;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.plugin.Command;
+import net.md_5.bungee.eaglercraft.BanList;
+
+public class CommandGlobalBanRegex extends Command {
+
+ private final boolean replaceBukkit;
+
+ public CommandGlobalBanRegex(boolean replaceBukkit) {
+ super(replaceBukkit ? "ban-regex" : "eag-ban-regex", "bungeecord.command.eag.banregex", replaceBukkit ? new String[] { "eag-ban-regex", "e-ban-regex",
+ "gban-regex", "eag-banregex", "e-banregex", "gbanregex", "banregex" } : new String[] { "e-ban-regex", "gban-regex",
+ "eag-banregex", "e-banregex", "gbanregex" });
+ this.replaceBukkit = replaceBukkit;
+ }
+
+ @Override
+ public void execute(CommandSender p0, String[] p1) {
+ String w = (String) p0.getAttachment().get("banRegexWaitingToAdd");
+ if(w != null) {
+ List lst = (List)p0.getAttachment().get("banRegexWaitingToKick");
+ if(p1.length != 1 || (!p1[0].equalsIgnoreCase("confirm") && !p1[0].equalsIgnoreCase("cancel"))) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Type " + ChatColor.WHITE + (replaceBukkit ? "/ban-regex" : "/eag-ban-regex") + " confirm" + ChatColor.RED + " to add regex " + ChatColor.WHITE + w +
+ ChatColor.RED + " and ban " + ChatColor.WHITE + lst.size() + ChatColor.RED + " players");
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Type " + ChatColor.WHITE + (replaceBukkit ? "/ban-regex" : "/eag-ban-regex") + " cancel" + ChatColor.RED + " to cancel this operation");
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.YELLOW + "Note: all usernames are converted to lowercase before being matched");
+ }else {
+ if(p1[0].equalsIgnoreCase("confirm")) {
+ if(BanList.banRegex(w)) {
+ for(ProxiedPlayer pp : lst) {
+ pp.disconnect("" + ChatColor.RED + "You are banned.\n" + ChatColor.DARK_GRAY + "Reason: banned by regex");
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Kicked: " + ChatColor.WHITE + pp.getName());
+ }
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Added regex '" + ChatColor.WHITE + w + ChatColor.GREEN + "' to the ban list");
+ }else {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Regex '" + ChatColor.WHITE + w + ChatColor.RED + "' is already banned");
+ }
+ }else {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Canceled ban");
+ }
+ p0.getAttachment().remove("banRegexWaitingToAdd");
+ p0.getAttachment().remove("banRegexWaitingToKick");
+ }
+ return;
+ }
+ if(p1.length != 1) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "How to use: " + ChatColor.WHITE + (replaceBukkit ? "/ban-regex" : "/eag-ban-regex") + " ");
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.YELLOW + "Note: all usernames are converted to lowercase before being matched");
+ return;
+ }
+ Pattern p;
+ try {
+ p = Pattern.compile(p1[0]);
+ }catch(Throwable t) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Regex syntax error: " + t.getMessage());
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.YELLOW + "Note: all usernames are converted to lowercase before being matched");
+ return;
+ }
+ boolean isSenderGonnaGetKicked = false;
+ List usersThatAreGonnaBeKicked = new ArrayList();
+ for(ProxiedPlayer pp : BungeeCord.getInstance().getPlayers()) {
+ String n = pp.getName().toLowerCase();
+ if(p.matcher(n).matches()) {
+ usersThatAreGonnaBeKicked.add(pp);
+ if(n.equalsIgnoreCase(p0.getName())) {
+ isSenderGonnaGetKicked = true;
+ break;
+ }
+ }
+ }
+ if(isSenderGonnaGetKicked) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "banning regex '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is gonna ban your own username");
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.YELLOW + "Note: all usernames are converted to lowercase before being matched");
+ return;
+ }
+ if(usersThatAreGonnaBeKicked.size() > 1) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "WARNING: banning regex '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is gonna ban " +
+ ChatColor.WHITE + usersThatAreGonnaBeKicked.size() + ChatColor.RED + " players off of your server");
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Type " + ChatColor.WHITE + (replaceBukkit ? "/ban-regex" : "/eag-ban-regex") + " confirm" + ChatColor.RED + " to continue, or type " +
+ ChatColor.WHITE + "/eag-ban-regex cancel" + ChatColor.RED + " to cancel");
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.YELLOW + "Note: all usernames are converted to lowercase before being matched");
+ p0.getAttachment().put("banRegexWaitingToKick", usersThatAreGonnaBeKicked);
+ p0.getAttachment().put("banRegexWaitingToAdd", p1[0]);
+ }else {
+ if(BanList.banRegex(p1[0])) {
+ if(usersThatAreGonnaBeKicked.size() > 0) {
+ usersThatAreGonnaBeKicked.get(0).disconnect("" + ChatColor.RED + "You are banned.\n" + ChatColor.DARK_GRAY + "Reason: banned by regex");
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Kicked: " + ChatColor.WHITE + usersThatAreGonnaBeKicked.get(0).getName());
+ }
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Added regex '" + ChatColor.WHITE + p1[0] + ChatColor.GREEN + "' to the ban list");
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.YELLOW + "Note: all usernames are converted to lowercase before being matched");
+ }else {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Regex '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is already banned");
+ }
+ }
+ }
+
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanReload.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanReload.java
new file mode 100644
index 0000000..3063b31
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanReload.java
@@ -0,0 +1,21 @@
+package net.md_5.bungee.command;
+
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.plugin.Command;
+import net.md_5.bungee.eaglercraft.BanList;
+
+public class CommandGlobalBanReload extends Command {
+
+ public CommandGlobalBanReload(boolean replaceBukkit) {
+ super(replaceBukkit ? "reloadban" : "eag-reloadban", "bungeecord.command.eag.reloadban", replaceBukkit ? new String[] { "eag-reloadban", "banreload", "eag-banreload", "e-reloadban",
+ "e-banreload", "gbanreload", "greloadban"} : new String[] { "eag-banreload", "e-reloadban", "e-banreload", "gbanreload", "greloadban"});
+ }
+
+ @Override
+ public void execute(CommandSender p0, String[] p1) {
+ BanList.maybeReloadBans(p0);
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.WHITE + "Ban list reloaded");
+ }
+
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanWildcard.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanWildcard.java
new file mode 100644
index 0000000..1d4783a
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalBanWildcard.java
@@ -0,0 +1,125 @@
+package net.md_5.bungee.command;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.md_5.bungee.BungeeCord;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.plugin.Command;
+import net.md_5.bungee.eaglercraft.BanList;
+
+public class CommandGlobalBanWildcard extends Command {
+
+ private final boolean replaceBukkit;
+
+ public CommandGlobalBanWildcard(boolean replaceBukkit) {
+ super(replaceBukkit ? "ban-wildcard" : "eag-ban-wildcard", "bungeecord.command.eag.banwildcard", replaceBukkit ? new String[] { "eag-ban-wildcard", "e-ban-wildcard", "gban-wildcard",
+ "banwildcard", "eag-banwildcard", "banwildcard"} : new String[] { "e-ban-wildcard", "gban-wildcard", "eag-banwildcard"});
+ this.replaceBukkit = replaceBukkit;
+ }
+
+ @Override
+ public void execute(CommandSender p0, String[] p1) {
+ String w = (String) p0.getAttachment().get("banWildcardWaitingToAdd");
+ if(w != null) {
+ List lst = (List)p0.getAttachment().get("banWildcardWaitingToKick");
+ if(p1.length != 1 || (!p1[0].equalsIgnoreCase("confirm") && !p1[0].equalsIgnoreCase("cancel"))) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Type " + ChatColor.WHITE + (replaceBukkit ? "/ban-wildcard" : "/eag-ban-wildcard") + " confirm" + ChatColor.RED + " to add wildcard " + ChatColor.WHITE + w +
+ ChatColor.RED + " and ban " + ChatColor.WHITE + lst.size() + ChatColor.RED + " players");
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Type " + ChatColor.WHITE + (replaceBukkit ? "/ban-wildcard" : "/eag-ban-wildcard") + " cancel" + ChatColor.RED + " to cancel this operation");
+ }else {
+ if(p1[0].equalsIgnoreCase("confirm")) {
+ if(BanList.banWildcard(w)) {
+ for(ProxiedPlayer pp : lst) {
+ pp.disconnect("" + ChatColor.RED + "You are banned.\n" + ChatColor.DARK_GRAY + "Reason: banned by wildcard");
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Kicked: " + ChatColor.WHITE + pp.getName());
+ }
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Added wildcard '" + ChatColor.WHITE + w + ChatColor.GREEN + "' to the ban list");
+ }else {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Wildcard '" + ChatColor.WHITE + w + ChatColor.RED + "' is already banned");
+ }
+ }else {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Canceled ban");
+ }
+ p0.getAttachment().remove("banWildcardWaitingToAdd");
+ p0.getAttachment().remove("banWildcardWaitingToKick");
+ }
+ return;
+ }
+ if(p1.length != 1) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "How to use: " + ChatColor.WHITE + (replaceBukkit ? "/ban-wildcard" : "/eag-ban-wildcard") + " ");
+ return;
+ }
+ p1[0] = p1[0].toLowerCase();
+ String s = p1[0];
+ boolean startStar = s.startsWith("*");
+ if(startStar) {
+ s = s.substring(1);
+ }
+ boolean endStar = s.endsWith("*");
+ if(endStar) {
+ s = s.substring(0, s.length() - 1);
+ }
+ if(!startStar && !endStar) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "'" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is not a wildcard, try '"
+ + ChatColor.WHITE + "*" + p1[0] + ChatColor.RED + "' or '" + ChatColor.WHITE + p1[0] + "*" + ChatColor.RED + "' or '" + ChatColor.WHITE
+ + "*" + p1[0] + "*" + ChatColor.RED + "' instead");
+ return;
+ }
+ boolean isSenderGonnaGetKicked = false;
+ List usersThatAreGonnaBeKicked = new ArrayList();
+ for(ProxiedPlayer pp : BungeeCord.getInstance().getPlayers()) {
+ String n = pp.getName().toLowerCase();
+ if(startStar && endStar) {
+ if(n.contains(s)) {
+ usersThatAreGonnaBeKicked.add(pp);
+ if(pp.getName().equalsIgnoreCase(p0.getName())) {
+ isSenderGonnaGetKicked = true;
+ break;
+ }
+ }
+ }else if(startStar) {
+ if(n.endsWith(s)) {
+ usersThatAreGonnaBeKicked.add(pp);
+ if(pp.getName().equalsIgnoreCase(p0.getName())) {
+ isSenderGonnaGetKicked = true;
+ break;
+ }
+ }
+ }else if(endStar) {
+ if(n.startsWith(s)) {
+ usersThatAreGonnaBeKicked.add(pp);
+ if(pp.getName().equalsIgnoreCase(p0.getName())) {
+ isSenderGonnaGetKicked = true;
+ break;
+ }
+ }
+ }
+ }
+ if(isSenderGonnaGetKicked) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "banning wildcard '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is gonna ban your own username");
+ return;
+ }
+ if(usersThatAreGonnaBeKicked.size() > 1) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "WARNING: banning wildcard '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is gonna ban " +
+ ChatColor.WHITE + usersThatAreGonnaBeKicked.size() + ChatColor.RED + " players off of your server");
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Type " + ChatColor.WHITE + (replaceBukkit ? "/ban-wildcard" : "/eag-ban-wildcard") + " confirm" + ChatColor.RED + " to continue, or type " +
+ ChatColor.WHITE + (replaceBukkit ? "/ban-wildcard" : "/eag-ban-wildcard") + " cancel" + ChatColor.RED + " to cancel");
+ p0.getAttachment().put("banWildcardWaitingToKick", usersThatAreGonnaBeKicked);
+ p0.getAttachment().put("banWildcardWaitingToAdd", p1[0]);
+ }else {
+ if(BanList.banWildcard(p1[0])) {
+ if(usersThatAreGonnaBeKicked.size() > 0) {
+ usersThatAreGonnaBeKicked.get(0).disconnect("" + ChatColor.RED + "You are banned.\n" + ChatColor.DARK_GRAY + "Reason: banned by wildcard");
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Kicked: " + ChatColor.WHITE + usersThatAreGonnaBeKicked.get(0).getName());
+ }
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Added wildcard '" + ChatColor.WHITE + p1[0] + ChatColor.GREEN + "' to the ban list");
+ }else {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Wildcard '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is already banned");
+ }
+ }
+ }
+
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalCheckBan.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalCheckBan.java
new file mode 100644
index 0000000..be2bd85
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalCheckBan.java
@@ -0,0 +1,56 @@
+package net.md_5.bungee.command;
+
+import java.net.InetAddress;
+
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.plugin.Command;
+import net.md_5.bungee.eaglercraft.BanList;
+import net.md_5.bungee.eaglercraft.BanList.BanCheck;
+import net.md_5.bungee.eaglercraft.BanList.BanState;
+
+public class CommandGlobalCheckBan extends Command {
+
+ private final boolean replaceBukkit;
+
+ public CommandGlobalCheckBan(boolean replaceBukkit) {
+ super(replaceBukkit ? "banned" : "eag-bannned", "bungeecord.command.eag.banned", replaceBukkit ? new String[] { "eag-banned", "isbanned", "e-banned", "gbanned", "eag-isbanned", "e-isbanned", "gisbanned" } :
+ new String[] { "e-banned", "gbanned", "eag-isbanned", "e-isbanned", "gisbanned" });
+ this.replaceBukkit = replaceBukkit;
+ }
+
+ @Override
+ public void execute(CommandSender p0, String[] p1) {
+ if(p1.length != 1) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "To check if a player or IP is banned, use: " + ChatColor.WHITE + (replaceBukkit ? "/banned" : "/eag-banned") + " ");
+ }else {
+ BanCheck bc = BanList.checkBanned(p1[0]);
+ if(!bc.isBanned()) {
+ try {
+ InetAddress addr = InetAddress.getByName(p1[0]);
+ bc = BanList.checkIpBanned(addr);
+ if(bc.isBanned()) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "IP address '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is banned by: "
+ + "'" + ChatColor.WHITE + bc.match + ChatColor.RED + "' " + ChatColor.YELLOW + "(" + bc.string + ")");
+ return;
+ }
+ }catch(Throwable t) {
+ // no
+ }
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Player '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' has not been banned");
+ }else {
+ if(bc.reason == BanState.USER_BANNED) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Player '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is banned by username, reason: "
+ + ChatColor.YELLOW + "\"" + bc.string + "\"");
+ }else if(bc.reason == BanState.WILDCARD_BANNED) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Player '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is banned by wildcard: "
+ + ChatColor.WHITE + "\"" + bc.match + "\"");
+ }else if(bc.reason == BanState.REGEX_BANNED) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Player '" + ChatColor.WHITE + p1[0] + ChatColor.RED + "' is banned by regex: "
+ + ChatColor.WHITE + "\"" + bc.match + "\"");
+ }
+ }
+ }
+ }
+
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalListBan.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalListBan.java
new file mode 100644
index 0000000..05d830a
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalListBan.java
@@ -0,0 +1,64 @@
+package net.md_5.bungee.command;
+
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.plugin.Command;
+import net.md_5.bungee.eaglercraft.BanList;
+
+public class CommandGlobalListBan extends Command {
+
+ private final boolean replaceBukkit;
+
+ public CommandGlobalListBan(boolean replaceBukkit) {
+ super(replaceBukkit ? "banlist" : "eag-banlist", "bungeecord.command.eag.banlist", replaceBukkit ? new String[] { "eag-banlist", "gbanlist", "e-banlist",
+ "gbanlist" } : new String[] { "gbanlist", "e-banlist" });
+ this.replaceBukkit = replaceBukkit;
+ }
+
+ @Override
+ public void execute(CommandSender p0, String[] p1) {
+ if(p1.length == 0 || (p1.length == 1 && (p1[0].equalsIgnoreCase("user") || p1[0].equalsIgnoreCase("username")
+ || p1[0].equalsIgnoreCase("users") || p1[0].equalsIgnoreCase("usernames")))) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Players banned by username: " + ChatColor.WHITE + BanList.listAllBans());
+ return;
+ }else if(p1.length == 1) {
+ if(p1[0].equalsIgnoreCase("regex") || p1[0].equalsIgnoreCase("regexes")) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Regex ban list: " + ChatColor.WHITE + BanList.listAllRegexBans());
+ return;
+ }else if(p1[0].equalsIgnoreCase("wildcard") || p1[0].equalsIgnoreCase("wildcards")) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Wildcard ban list: " + ChatColor.WHITE + BanList.listAllWildcardBans());
+ return;
+ }else if(p1[0].equalsIgnoreCase("ip") || p1[0].equalsIgnoreCase("ips")) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "To list IP bans, use: " + ChatColor.WHITE + (replaceBukkit ? "/banlist" : "/eag-banlist") + " ip [v4|v6]");
+ return;
+ }
+ }else if(p1.length > 1 && p1.length <= 3 && (p1[0].equalsIgnoreCase("ip") || p1[0].equalsIgnoreCase("ips"))) {
+ int addrOrNetmask = 0;
+ if(p1[1].equalsIgnoreCase("addr") || p1[1].equalsIgnoreCase("addrs")) {
+ addrOrNetmask = 1;
+ }else if(p1[1].equalsIgnoreCase("netmask") || p1[1].equalsIgnoreCase("netmasks")) {
+ addrOrNetmask = 2;
+ }
+ if(addrOrNetmask > 0) {
+ boolean yes = false;
+ if(p1.length == 2 || (p1.length == 3 && (p1[2].equalsIgnoreCase("v4") || p1[2].equals("4")))) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "IPv4 " + (addrOrNetmask == 2 ? "netmask" : "address") + " ban list: " + ChatColor.WHITE + BanList.listAllIPBans(false, addrOrNetmask == 2));
+ yes = true;
+ }
+ if(p1.length == 2 || (p1.length == 3 && (p1[2].equalsIgnoreCase("v6") || p1[2].equals("6")))) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "IPv6 " + (addrOrNetmask == 2 ? "netmask" : "address") + " ban list: " + ChatColor.WHITE + BanList.listAllIPBans(true, addrOrNetmask == 2));
+ yes = true;
+ }
+ if(yes) {
+ return;
+ }
+ }
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "To list IP bans, use: " + ChatColor.WHITE + (replaceBukkit ? "/banlist" : "/eag-banlist") + " ip [v4|v6]");
+ return;
+ }
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "To list all user bans, use: " + ChatColor.WHITE + (replaceBukkit ? "/banlist" : "/eag-banlist"));
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "To list ips, regexes, and wildcards, use: " + ChatColor.WHITE + (replaceBukkit ? "/banlist" : "/eag-banlist") + " ");
+ return;
+ }
+
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalUnban.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalUnban.java
new file mode 100644
index 0000000..aac2039
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandGlobalUnban.java
@@ -0,0 +1,56 @@
+package net.md_5.bungee.command;
+
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.plugin.Command;
+import net.md_5.bungee.eaglercraft.BanList;
+
+public class CommandGlobalUnban extends Command {
+
+ private final boolean replaceBukkit;
+
+ public CommandGlobalUnban(boolean replaceBukkit) {
+ super(replaceBukkit ? "unban" : "eag-unban", "bungeecord.command.eag.unban", replaceBukkit ? new String[] {"eag-unban", "e-unban", "gunban"} :new String[] {"e-unban", "gunban"});
+ this.replaceBukkit = replaceBukkit;
+ }
+
+ @Override
+ public void execute(CommandSender p0, String[] p1) {
+ if(p1.length != 2 || (!p1[0].equalsIgnoreCase("user") && !p1[0].equalsIgnoreCase("username") && !p1[0].equalsIgnoreCase("player")
+ && !p1[0].equalsIgnoreCase("wildcard") && !p1[0].equalsIgnoreCase("regex") && !p1[0].equalsIgnoreCase("ip"))) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "To unban a player, use: " + ChatColor.WHITE + "/" + (replaceBukkit?"":"eag-") + "unban user ");
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "To unban an ip/wildcard/regex, use: " + ChatColor.WHITE + "/" + (replaceBukkit?"":"eag-") + "unban ");
+ return;
+ }
+ if(p1[0].equalsIgnoreCase("user") || p1[0].equalsIgnoreCase("username") || p1[0].equalsIgnoreCase("player")) {
+ if(BanList.unban(p1[1])) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "User '" + ChatColor.WHITE + p1[1] + ChatColor.GREEN + "' was unbanned");
+ }else {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "User '" + ChatColor.WHITE + p1[1] + ChatColor.RED + "' is not banned");
+ }
+ }else if(p1[0].equalsIgnoreCase("ip")) {
+ try {
+ if(BanList.unbanIP(p1[1])) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "IP '" + ChatColor.WHITE + p1[1] + ChatColor.GREEN + "' was unbanned");
+ }else {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "IP '" + ChatColor.WHITE + p1[1] + ChatColor.RED + "' is not banned");
+ }
+ }catch(Throwable t) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "IP address '" + ChatColor.WHITE + p1[1] + ChatColor.RED + "' is invalid: " + t.getMessage());
+ }
+ }else if(p1[0].equalsIgnoreCase("wildcard")) {
+ if(BanList.unbanWildcard(p1[1])) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Wildcard '" + ChatColor.WHITE + p1[1] + ChatColor.GREEN + "' was unbanned");
+ }else {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Wildcard '" + ChatColor.WHITE + p1[1] + ChatColor.RED + "' is not banned");
+ }
+ }else if(p1[0].equalsIgnoreCase("regex")) {
+ if(BanList.unbanRegex(p1[1])) {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.GREEN + "Regex '" + ChatColor.WHITE + p1[1] + ChatColor.GREEN + "' was unbanned");
+ }else {
+ p0.sendMessage(BanList.banChatMessagePrefix + ChatColor.RED + "Regex '" + ChatColor.WHITE + p1[1] + ChatColor.RED + "' is not banned");
+ }
+ }
+ }
+
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandIP.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandIP.java
new file mode 100644
index 0000000..ac30aa6
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandIP.java
@@ -0,0 +1,34 @@
+package net.md_5.bungee.command;
+
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.plugin.Command;
+
+public class CommandIP extends Command
+{
+
+ public CommandIP()
+ {
+ super( "ip", "bungeecord.command.ip" );
+ }
+
+ @Override
+ public void execute(CommandSender sender, String[] args)
+ {
+ if ( args.length < 1 )
+ {
+ sender.sendMessage( ChatColor.RED + "Please follow this command by a user name" );
+ return;
+ }
+ ProxiedPlayer user = ProxyServer.getInstance().getPlayer( args[0] );
+ if ( user == null )
+ {
+ sender.sendMessage( ChatColor.RED + "That user is not online" );
+ } else
+ {
+ sender.sendMessage( ChatColor.BLUE + "IP of " + args[0] + " is " + user.getAddress() );
+ }
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandList.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandList.java
new file mode 100644
index 0000000..ae65f9d
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandList.java
@@ -0,0 +1,47 @@
+package net.md_5.bungee.command;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import net.md_5.bungee.Util;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.config.ServerInfo;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.plugin.Command;
+
+/**
+ * Command to list all players connected to the proxy.
+ */
+public class CommandList extends Command
+{
+
+ public CommandList()
+ {
+ super( "glist", "bungeecord.command.list" );
+ }
+
+ @Override
+ public void execute(CommandSender sender, String[] args)
+ {
+ for ( ServerInfo server : ProxyServer.getInstance().getServers().values() )
+ {
+ if ( !server.canAccess( sender ) )
+ {
+ continue;
+ }
+
+ List players = new ArrayList<>();
+ for ( ProxiedPlayer player : server.getPlayers() )
+ {
+ players.add( player.getDisplayName() );
+ }
+ Collections.sort( players, String.CASE_INSENSITIVE_ORDER );
+
+ sender.sendMessage( ProxyServer.getInstance().getTranslation( "command_list", server.getName(), server.getPlayers().size(), Util.format( players, ChatColor.RESET + ", " ) ) );
+ }
+
+ sender.sendMessage( ProxyServer.getInstance().getTranslation( "total_players", ProxyServer.getInstance().getOnlineCount() ) );
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandPerms.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandPerms.java
new file mode 100644
index 0000000..610825c
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandPerms.java
@@ -0,0 +1,34 @@
+package net.md_5.bungee.command;
+
+import java.util.HashSet;
+import java.util.Set;
+import net.md_5.bungee.Util;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.plugin.Command;
+
+public class CommandPerms extends Command
+{
+
+ public CommandPerms()
+ {
+ super( "perms" );
+ }
+
+ @Override
+ public void execute(CommandSender sender, String[] args)
+ {
+ Set permissions = new HashSet<>();
+ for ( String group : sender.getGroups() )
+ {
+ permissions.addAll( ProxyServer.getInstance().getConfigurationAdapter().getPermissions( group ) );
+ }
+ sender.sendMessage( ChatColor.GOLD + "You have the following groups: " + Util.csv( sender.getGroups() ) );
+
+ for ( String permission : permissions )
+ {
+ sender.sendMessage( ChatColor.BLUE + "- " + permission );
+ }
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandReload.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandReload.java
new file mode 100644
index 0000000..437f010
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandReload.java
@@ -0,0 +1,25 @@
+package net.md_5.bungee.command;
+
+import net.md_5.bungee.BungeeCord;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.plugin.Command;
+
+public class CommandReload extends Command
+{
+
+ public CommandReload()
+ {
+ super( "greload", "bungeecord.command.reload" );
+ }
+
+ @Override
+ public void execute(CommandSender sender, String[] args)
+ {
+ BungeeCord.getInstance().config.load();
+ BungeeCord.getInstance().stopListeners();
+ BungeeCord.getInstance().startListeners();
+ sender.sendMessage( ChatColor.BOLD.toString() + ChatColor.RED.toString() + "BungeeCord has been reloaded."
+ + " This is NOT advisable and you will not be supported with any issues that arise! Please restart BungeeCord ASAP." );
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandSend.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandSend.java
new file mode 100644
index 0000000..527914f
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandSend.java
@@ -0,0 +1,72 @@
+package net.md_5.bungee.command;
+
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.config.ServerInfo;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.plugin.Command;
+
+public class CommandSend extends Command
+{
+
+ public CommandSend()
+ {
+ super( "send", "bungeecord.command.send" );
+ }
+
+ @Override
+ public void execute(CommandSender sender, String[] args)
+ {
+ if ( args.length != 2 )
+ {
+ sender.sendMessage( ChatColor.RED + "Not enough arguments, usage: /send " );
+ return;
+ }
+ ServerInfo target = ProxyServer.getInstance().getServerInfo( args[1] );
+ if ( target == null )
+ {
+ sender.sendMessage( ProxyServer.getInstance().getTranslation( "no_server" ) );
+ return;
+ }
+
+ if ( args[0].equalsIgnoreCase( "all" ) )
+ {
+ for ( ProxiedPlayer p : ProxyServer.getInstance().getPlayers() )
+ {
+ summon( p, target, sender );
+ }
+ } else if ( args[0].equalsIgnoreCase( "current" ) )
+ {
+ if ( !( sender instanceof ProxiedPlayer ) )
+ {
+ sender.sendMessage( ChatColor.RED + "Only in game players can use this command" );
+ return;
+ }
+ ProxiedPlayer player = (ProxiedPlayer) sender;
+ for ( ProxiedPlayer p : player.getServer().getInfo().getPlayers() )
+ {
+ summon( p, target, sender );
+ }
+ } else
+ {
+ ProxiedPlayer player = ProxyServer.getInstance().getPlayer( args[0] );
+ if ( player == null )
+ {
+ sender.sendMessage( ChatColor.RED + "That player is not online" );
+ return;
+ }
+ summon( player, target, sender );
+ }
+ sender.sendMessage( ChatColor.GREEN + "Successfully summoned player(s)" );
+ }
+
+ private void summon(ProxiedPlayer player, ServerInfo target, CommandSender sender)
+ {
+ if ( player.getServer() != null && !player.getServer().getInfo().equals( target ) )
+ {
+ player.connect( target );
+ player.sendMessage( ChatColor.GOLD + "Summoned to " + target.getName() + " by " + sender.getName() );
+ }
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandServer.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandServer.java
new file mode 100644
index 0000000..5ee09a3
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/CommandServer.java
@@ -0,0 +1,88 @@
+package net.md_5.bungee.command;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import java.util.Collections;
+import java.util.Map;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.config.ServerInfo;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.plugin.Command;
+import net.md_5.bungee.api.plugin.TabExecutor;
+
+/**
+ * Command to list and switch a player between available servers.
+ */
+public class CommandServer extends Command implements TabExecutor
+{
+
+ public CommandServer()
+ {
+ super( "server", "bungeecord.command.server" );
+ }
+
+ @Override
+ public void execute(CommandSender sender, String[] args)
+ {
+ if ( !( sender instanceof ProxiedPlayer ) )
+ {
+ return;
+ }
+ ProxiedPlayer player = (ProxiedPlayer) sender;
+ Map servers = ProxyServer.getInstance().getServers();
+ if ( args.length == 0 )
+ {
+ player.sendMessage( ProxyServer.getInstance().getTranslation( "current_server" ) + player.getServer().getInfo().getName() );
+
+ StringBuilder serverList = new StringBuilder();
+ for ( ServerInfo server : servers.values() )
+ {
+ if ( server.canAccess( player ) )
+ {
+ serverList.append( server.getName() );
+ serverList.append( ", " );
+ }
+ }
+ if ( serverList.length() != 0 )
+ {
+ serverList.setLength( serverList.length() - 2 );
+ }
+ player.sendMessage( ProxyServer.getInstance().getTranslation( "server_list" ) + serverList.toString() );
+ } else
+ {
+ ServerInfo server = servers.get( args[0] );
+ if ( server == null )
+ {
+ player.sendMessage( ProxyServer.getInstance().getTranslation( "no_server" ) );
+ } else if ( !server.canAccess( player ) )
+ {
+ player.sendMessage( ProxyServer.getInstance().getTranslation( "no_server_permission" ) );
+ } else
+ {
+ player.connect( server );
+ }
+ }
+ }
+
+ @Override
+ public Iterable onTabComplete(final CommandSender sender, String[] args)
+ {
+ return ( args.length != 0 ) ? Collections.EMPTY_LIST : Iterables.transform( Iterables.filter( ProxyServer.getInstance().getServers().values(), new Predicate()
+ {
+ @Override
+ public boolean apply(ServerInfo input)
+ {
+ return input.canAccess( sender );
+ }
+ } ), new Function()
+ {
+ @Override
+ public String apply(ServerInfo input)
+ {
+ return input.getName();
+ }
+ } );
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java
new file mode 100644
index 0000000..35670a1
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/ConsoleCommandSender.java
@@ -0,0 +1,83 @@
+package net.md_5.bungee.command;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.ProxyServer;
+
+/**
+ * Command sender representing the proxy console.
+ */
+public class ConsoleCommandSender implements CommandSender
+{
+ private static final Map attachment = new WeakHashMap();
+
+ private static final ConsoleCommandSender instance = new ConsoleCommandSender();
+
+ private ConsoleCommandSender()
+ {
+ }
+
+ public static ConsoleCommandSender getInstance(){
+ return instance;
+ }
+
+ @Override
+ public void sendMessage(String message)
+ {
+ ProxyServer.getInstance().getLogger().info( message );
+ }
+
+ @Override
+ public void sendMessages(String... messages)
+ {
+ for ( String message : messages )
+ {
+ sendMessage( message );
+ }
+ }
+
+ @Override
+ public String getName()
+ {
+ return "CONSOLE";
+ }
+
+ @Override
+ public Collection getGroups()
+ {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public void addGroups(String... groups)
+ {
+ throw new UnsupportedOperationException( "Console may not have groups" );
+ }
+
+ @Override
+ public void removeGroups(String... groups)
+ {
+ throw new UnsupportedOperationException( "Console may not have groups" );
+ }
+
+ @Override
+ public boolean hasPermission(String permission)
+ {
+ return true;
+ }
+
+ @Override
+ public void setPermission(String permission, boolean value)
+ {
+ throw new UnsupportedOperationException( "Console has all permissions" );
+ }
+
+ @Override
+ public Map getAttachment() {
+ return attachment;
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/command/PlayerCommand.java b/eaglerbungee/src/main/java/net/md_5/bungee/command/PlayerCommand.java
new file mode 100644
index 0000000..a0c9164
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/command/PlayerCommand.java
@@ -0,0 +1,45 @@
+package net.md_5.bungee.command;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.plugin.Command;
+import net.md_5.bungee.api.plugin.TabExecutor;
+
+public abstract class PlayerCommand extends Command implements TabExecutor
+{
+
+ public PlayerCommand(String name)
+ {
+ super( name );
+ }
+
+ public PlayerCommand(String name, String permission, String... aliases)
+ {
+ super( name, permission, aliases );
+ }
+
+ @Override
+ public Iterable onTabComplete(CommandSender sender, String[] args)
+ {
+ final String lastArg = ( args.length > 0 ) ? args[args.length - 1] : "";
+ return Iterables.transform( Iterables.filter( ProxyServer.getInstance().getPlayers(), new Predicate()
+ {
+ @Override
+ public boolean apply(ProxiedPlayer player)
+ {
+ return player.getName().startsWith( lastArg );
+ }
+ } ), new Function()
+ {
+ @Override
+ public String apply(ProxiedPlayer player)
+ {
+ return player.getName();
+ }
+ } );
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/config/Configuration.java b/eaglerbungee/src/main/java/net/md_5/bungee/config/Configuration.java
new file mode 100644
index 0000000..1f4102f
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/config/Configuration.java
@@ -0,0 +1,181 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.config;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.UUID;
+
+import com.google.common.base.Preconditions;
+
+import gnu.trove.map.TMap;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.config.AuthServiceInfo;
+import net.md_5.bungee.api.config.ConfigurationAdapter;
+import net.md_5.bungee.api.config.ListenerInfo;
+import net.md_5.bungee.api.config.ServerInfo;
+import net.md_5.bungee.eaglercraft.EaglercraftBungee;
+import net.md_5.bungee.util.CaseInsensitiveMap;
+
+public class Configuration {
+ private int timeout;
+ private String uuid;
+ private Collection listeners;
+ private TMap servers;
+ private AuthServiceInfo authInfo;
+ private boolean onlineMode;
+ private boolean voiceEnabled;
+ private boolean protocolSupport;
+ private String tokenVerify;
+ private int playerLimit;
+ private String name;
+ private boolean showBanType;
+ private boolean blacklistOfflineDownload;
+ private boolean blacklistReplits;
+ private boolean blacklistOriginless;
+ private boolean simpleWhitelistEnabled;
+ private boolean acceptBukkitConsoleCommandPacket;
+ private Collection disabledCommands;
+ private Collection iceServers;
+ private boolean bungeeOnBungee;
+
+ public Configuration() {
+ this.timeout = 30000;
+ this.uuid = UUID.randomUUID().toString();
+ this.onlineMode = true;
+ this.playerLimit = -1;
+ }
+
+ public void load() {
+ final ConfigurationAdapter adapter = ProxyServer.getInstance().getConfigurationAdapter();
+ adapter.load();
+ this.listeners = adapter.getListeners();
+ this.timeout = adapter.getInt("timeout", this.timeout);
+ this.uuid = adapter.getString("stats", this.uuid);
+ if(this.uuid.equalsIgnoreCase("595698b3-9c36-4e86-b1ee-cb3027038f41")) {
+ this.uuid = UUID.randomUUID().toString();
+ System.err.println("Notice: this server has the stats UUID \"595698b3-9c36-4e86-b1ee-cb3027038f41\" which is a known duplicate");
+ System.err.println("It has been updated to \"" + this.uuid + "\". This is not an error");
+ adapter.getMap().put("stats", this.uuid);
+ adapter.forceSave();
+ }
+ this.authInfo = adapter.getAuthSettings();
+ this.onlineMode = false;
+ this.voiceEnabled = adapter.getBoolean("voice_enabled", true);
+ this.protocolSupport = adapter.getBoolean("protocol_support_fix", false);
+ this.tokenVerify = adapter.getString("token_verify", "");
+ this.playerLimit = adapter.getInt("player_limit", this.playerLimit);
+ this.name = adapter.getString("server_name", EaglercraftBungee.name + " Server");
+ this.showBanType = adapter.getBoolean("display_ban_type_on_kick", false);
+ this.blacklistOfflineDownload = adapter.getBoolean("origin_blacklist_block_offline_download", false);
+ this.blacklistReplits = adapter.getBoolean("origin_blacklist_block_replit_clients", false);
+ adapter.getMap().remove("origin_blacklist_block_missing_origin_header");
+ this.blacklistOriginless = adapter.getBoolean("origin_blacklist_block_invalid_origin_header", true);
+ this.simpleWhitelistEnabled = adapter.getBoolean("origin_blacklist_use_simple_whitelist", false);
+ this.acceptBukkitConsoleCommandPacket = adapter.getBoolean("accept_bukkit_console_command_packets", false);
+ this.bungeeOnBungee = adapter.getBoolean("bungee_on_bungee", false);
+ this.disabledCommands = adapter.getDisabledCommands();
+ this.iceServers = adapter.getICEServers();
+ Preconditions.checkArgument(this.listeners != null && !this.listeners.isEmpty(), (Object) "No listeners defined.");
+ final Map newServers = adapter.getServers();
+ Preconditions.checkArgument(newServers != null && !newServers.isEmpty(), (Object) "No servers defined");
+ if (this.servers == null) {
+ this.servers = (TMap) new CaseInsensitiveMap(newServers);
+ } else {
+ for (final ServerInfo oldServer : this.servers.values()) {
+ Preconditions.checkArgument(newServers.containsValue(oldServer), "Server %s removed on reload!", new Object[] { oldServer.getName() });
+ }
+ for (final Map.Entry newServer : newServers.entrySet()) {
+ if (!this.servers.containsValue(newServer.getValue())) {
+ this.servers.put(newServer.getKey(), newServer.getValue());
+ }
+ }
+ }
+ for (final ListenerInfo listener : this.listeners) {
+ Preconditions.checkArgument(this.servers.containsKey((Object) listener.getDefaultServer()), "Default server %s is not defined", new Object[] { listener.getDefaultServer() });
+ }
+ }
+
+ public int getTimeout() {
+ return this.timeout;
+ }
+
+ public String getUuid() {
+ return this.uuid;
+ }
+
+ public Collection getListeners() {
+ return this.listeners;
+ }
+
+ public TMap getServers() {
+ return this.servers;
+ }
+
+ public boolean isOnlineMode() {
+ return this.onlineMode;
+ }
+
+ public int getPlayerLimit() {
+ return this.playerLimit;
+ }
+
+ public AuthServiceInfo getAuthInfo() {
+ return authInfo;
+ }
+
+ public boolean getVoiceEnabled() {
+ return voiceEnabled;
+ }
+
+ public boolean getProtocolSupport() {
+ return protocolSupport;
+ }
+
+ public String getTokenVerify() {
+ return tokenVerify;
+ }
+
+ public String getServerName() {
+ return name;
+ }
+
+ public boolean shouldShowBanType() {
+ return this.showBanType;
+ }
+
+ public boolean shouldBlacklistOfflineDownload() {
+ return blacklistOfflineDownload;
+ }
+
+ public boolean shouldBlacklistReplits() {
+ return blacklistReplits;
+ }
+
+ public boolean shouldBlacklistOriginless() {
+ return blacklistOriginless;
+ }
+
+ public boolean isSimpleWhitelistEnabled() {
+ return simpleWhitelistEnabled;
+ }
+
+ public boolean shouldAcceptBukkitConsoleCommandPacket() {
+ return acceptBukkitConsoleCommandPacket;
+ }
+
+ public Collection getDisabledCommands() {
+ return disabledCommands;
+ }
+
+ public Collection getICEServers() {
+ return iceServers;
+ }
+
+ public boolean allowBungeeOnBungee() {
+ return bungeeOnBungee;
+ }
+
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/config/YamlConfig.java b/eaglerbungee/src/main/java/net/md_5/bungee/config/YamlConfig.java
new file mode 100644
index 0000000..f65e110
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/config/YamlConfig.java
@@ -0,0 +1,400 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.config;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Writer;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+
+import net.md_5.bungee.BungeeCord;
+import net.md_5.bungee.Util;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.config.AuthServiceInfo;
+import net.md_5.bungee.api.config.ConfigurationAdapter;
+import net.md_5.bungee.api.config.ListenerInfo;
+import net.md_5.bungee.api.config.MOTDCacheConfiguration;
+import net.md_5.bungee.api.config.ServerInfo;
+import net.md_5.bungee.api.config.TexturePackInfo;
+import net.md_5.bungee.api.tab.TabListHandler;
+import net.md_5.bungee.eaglercraft.RedirectServerInfo;
+import net.md_5.bungee.eaglercraft.WebSocketRateLimiter;
+import net.md_5.bungee.tab.Global;
+import net.md_5.bungee.tab.GlobalPing;
+import net.md_5.bungee.tab.ServerUnique;
+import net.md_5.bungee.util.CaseInsensitiveMap;
+
+public class YamlConfig implements ConfigurationAdapter {
+ private Yaml yaml;
+ private Map config;
+ private final File file;
+
+ public YamlConfig() {
+ this.file = new File("config.yml");
+ }
+
+ @Override
+ public void load() {
+ try {
+ this.file.createNewFile();
+ final DumperOptions options = new DumperOptions();
+ options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+ this.yaml = new Yaml(options);
+ try (final InputStream is = new FileInputStream(this.file)) {
+ this.config = (Map) this.yaml.load(is);
+ }
+ if (this.config == null) {
+ this.config = (Map) new CaseInsensitiveMap();
+ } else {
+ this.config = (Map) new CaseInsensitiveMap(this.config);
+ }
+ } catch (IOException ex) {
+ throw new RuntimeException("Could not load configuration!", ex);
+ }
+ final Map> permissions = this.get("permissions", new HashMap>());
+ if (permissions.isEmpty()) {
+ permissions.put("default", Arrays.asList("bungeecord.command.server", "bungeecord.command.list", "bungeecord.command.eag.domain", "bungeecord.command.eag.changepassword"));
+ permissions.put("admin", Arrays.asList("bungeecord.command.alert", "bungeecord.command.end", "bungeecord.command.ip", "bungeecord.command.reload",
+ "bungeecord.command.eag.ban", "bungeecord.command.eag.banwildcard", "bungeecord.command.eag.banip", "bungeecord.command.eag.banregex",
+ "bungeecord.command.eag.reloadban", "bungeecord.command.eag.banned", "bungeecord.command.eag.banlist", "bungeecord.command.eag.unban", "bungeecord.command.eag.ratelimit",
+ "bungeecord.command.eag.blockdomain", "bungeecord.command.eag.blockdomainname", "bungeecord.command.eag.unblockdomain"));
+ } else if (this.get("authservice", new HashMap()).isEmpty() && permissions.containsKey("default") && !permissions.get("default").contains("bungeecord.command.eag.changepassword")) {
+ permissions.get("default").add("bungeecord.command.eag.changepassword");
+ }
+ this.get("groups", new HashMap());
+ }
+
+ private T get(final String path, final T def) {
+ return this.get(path, def, this.config);
+ }
+
+ private T get(final String path, final T def, final Map submap) {
+ final int index = path.indexOf(46);
+ if (index == -1) {
+ Object val = submap.get(path);
+ if (val == null && def != null) {
+ val = def;
+ submap.put(path, def);
+ this.save();
+ }
+ return (T) val;
+ }
+ final String first = path.substring(0, index);
+ final String second = path.substring(index + 1, path.length());
+ Map sub = (Map) submap.get(first);
+ if (sub == null) {
+ sub = new LinkedHashMap();
+ submap.put(first, sub);
+ }
+ return (T) this.get(second, (Object) def, sub);
+ }
+
+ private void save() {
+ try (final FileWriter wr = new FileWriter(this.file)) {
+ this.yaml.dump((Object) this.config, (Writer) wr);
+ } catch (IOException ex) {
+ ProxyServer.getInstance().getLogger().log(Level.WARNING, "Could not save config", ex);
+ }
+ }
+
+ @Override
+ public int getInt(final String path, final int def) {
+ return this.get(path, def);
+ }
+
+ @Override
+ public String getString(final String path, final String def) {
+ return this.get(path, def);
+ }
+
+ @Override
+ public boolean getBoolean(final String path, final boolean def) {
+ return this.get(path, def);
+ }
+
+ @Override
+ public Map getServers() {
+ final Map base = this.get("servers", (Map) Collections.singletonMap("lobby", new HashMap()));
+ final Map ret = new HashMap();
+ for (final Map.Entry entry : base.entrySet()) {
+ final Map val = entry.getValue();
+ final String name = entry.getKey();
+ if(val.containsKey("redirect")) {
+ final String addr = this.get("redirect", "ws://someOtherServer/", val);
+ final boolean restricted = this.get("restricted", false, val);
+ final ServerInfo info = new RedirectServerInfo(name, addr, restricted);
+ ret.put(name, info);
+ }else {
+ final String addr = this.get("address", "localhost:25501", val);
+ final boolean restricted = this.get("restricted", false, val);
+ final InetSocketAddress address = Util.getAddr(addr);
+ final ServerInfo info = ProxyServer.getInstance().constructServerInfo(name, address, restricted);
+ ret.put(name, info);
+ }
+ }
+ return ret;
+ }
+
+ @Override
+ public Collection getListeners() {
+ final Collection base = this.get("listeners", (Collection) Arrays.asList(new HashMap()));
+ final Map forcedDef = new HashMap();
+ //forcedDef.put("pvp.md-5.net", "pvp");
+ final Collection ret = new HashSet();
+ for (final Map val : base) {
+ String motd = this.get("motd", null, val);
+ if(motd != null) {
+ val.remove("motd");
+ }
+ motd = this.get("motd1", motd, val);
+ if(motd == null) {
+ motd = this.get("motd1", "&6An Eaglercraft server", val);
+ }
+ motd = ChatColor.translateAlternateColorCodes('&', motd);
+ String motd2 = this.get("motd2", null, val);
+ if(motd2 != null && motd2.length() > 0) {
+ motd = motd + "\n" + ChatColor.translateAlternateColorCodes('&', motd2);
+ }
+ final int maxPlayers = this.get("max_players", 60, val);
+ final String defaultServer = this.get("default_server", "lobby", val);
+ final String fallbackServer = this.get("fallback_server", defaultServer, val);
+ final boolean forceDefault = this.get("force_default_server", true, val);
+ final boolean websocket = this.get("websocket", true, val);
+ final boolean forwardIp = this.get("forward_ip", false, val);
+ final String forwardIpHeader = this.get("forward_ip_header", "X-Real-IP", val);
+ final String host = this.get("host", "0.0.0.0:25565", val);
+ final String javaHost = this.get("java_host", "null", val);
+ final int tabListSize = this.get("tab_size", 60, val);
+ final InetSocketAddress address = Util.getAddr(host);
+ final InetSocketAddress javaAddress = (javaHost.equalsIgnoreCase("null") || javaHost == null) ? null : Util.getAddr(javaHost);
+ final Map forced = (Map) new CaseInsensitiveMap(this.get("forced_hosts", forcedDef, val));
+ final String textureURL = this.get("texture_url", (String) null, val);
+ final int textureSize = this.get("texture_size", 16, val);
+ final TexturePackInfo texture = (textureURL == null) ? null : new TexturePackInfo(textureURL, textureSize);
+ final String tabListName = this.get("tab_list", "GLOBAL_PING", val);
+ final String serverIcon = this.get("server_icon", "server-icon.png", val);
+ final boolean allowMOTD = this.get("allow_motd", true, val);
+ final boolean allowQuery = this.get("allow_query", true, val);
+ final MOTDCacheConfiguration cacheConfig = readCacheConfiguration(this.get("request_motd_cache", new HashMap(), val));
+
+ WebSocketRateLimiter ratelimitIP = null;
+ WebSocketRateLimiter ratelimitLogin = null;
+ WebSocketRateLimiter ratelimitMOTD = null;
+ WebSocketRateLimiter ratelimitQuery = null;
+ final Map rateLimits = this.get("ratelimit", new HashMap(), val);
+ final Map ratelimitIPConfig = this.get("ip", new HashMap(), rateLimits);
+ final Map ratelimitLoginConfig = this.get("login", new HashMap(), rateLimits);
+ final Map ratelimitMOTDConfig = this.get("motd", new HashMap(), rateLimits);
+ final Map ratelimitQueryConfig = this.get("query", new HashMap(), rateLimits);
+
+ if(this.get("enable", true, ratelimitIPConfig)) {
+ ratelimitIP = new WebSocketRateLimiter(
+ this.get("period", 90, ratelimitIPConfig),
+ this.get("limit", 60, ratelimitIPConfig),
+ this.get("limit_lockout", 80, ratelimitIPConfig),
+ this.get("lockout_duration", 1200, ratelimitIPConfig),
+ this.get("exceptions", new ArrayList(), ratelimitIPConfig)
+ );
+ }
+ if(this.get("enable", true, ratelimitLoginConfig)) {
+ ratelimitLogin = new WebSocketRateLimiter(
+ this.get("period", 50, ratelimitLoginConfig),
+ this.get("limit", 5, ratelimitLoginConfig),
+ this.get("limit_lockout", 10, ratelimitLoginConfig),
+ this.get("lockout_duration", 300, ratelimitLoginConfig),
+ this.get("exceptions", new ArrayList(), ratelimitLoginConfig)
+ );
+ }
+ if(this.get("enable", true, ratelimitMOTDConfig)) {
+ ratelimitMOTD = new WebSocketRateLimiter(
+ this.get("period", 30, ratelimitMOTDConfig),
+ this.get("limit", 5, ratelimitMOTDConfig),
+ this.get("limit_lockout", 15, ratelimitMOTDConfig),
+ this.get("lockout_duration", 1200, ratelimitMOTDConfig),
+ this.get("exceptions", new ArrayList(), ratelimitMOTDConfig)
+ );
+ }
+ if(this.get("enable", true, ratelimitQueryConfig)) {
+ ratelimitQuery = new WebSocketRateLimiter(
+ this.get("period", 90, ratelimitQueryConfig),
+ this.get("limit", 60, ratelimitQueryConfig),
+ this.get("limit_lockout", 80, ratelimitQueryConfig),
+ this.get("lockout_duration", 1200, ratelimitQueryConfig),
+ this.get("exceptions", new ArrayList(), ratelimitQueryConfig)
+ );
+ }
+
+
+ DefaultTabList value = DefaultTabList.valueOf(tabListName.toUpperCase());
+ if (value == null) {
+ value = DefaultTabList.GLOBAL_PING;
+ }
+ ret.add(new ListenerInfo(host, address, javaAddress, forwardIpHeader, motd, maxPlayers, tabListSize, defaultServer,
+ fallbackServer, forceDefault, websocket, forwardIp, forced, texture, value.clazz, serverIcon, cacheConfig,
+ allowMOTD, allowQuery, ratelimitIP, ratelimitLogin, ratelimitMOTD, ratelimitQuery));
+ }
+ return ret;
+ }
+
+ private MOTDCacheConfiguration readCacheConfiguration(Map val) {
+ final int ttl = this.get("cache_ttl", 7200, val);
+ final boolean anim = this.get("online_server_list_animation", false, val);
+ final boolean results = this.get("online_server_list_results", true, val);
+ final boolean trending = this.get("online_server_list_trending", true, val);
+ final boolean portfolios = this.get("online_server_list_portfolios", false, val);
+ return new MOTDCacheConfiguration(ttl, anim, results, trending, portfolios);
+ }
+
+ @Override
+ public Collection getGroups(final String player) {
+ final Collection groups = this.get("groups." + player, (Collection) null);
+ final Collection ret = (groups == null) ? new HashSet() : new HashSet(groups);
+ ret.add("default");
+ return ret;
+ }
+
+ @Override
+ public Collection getPermissions(final String group) {
+ return this.get("permissions." + group, (Collection) Collections.EMPTY_LIST);
+ }
+
+ private enum DefaultTabList {
+ GLOBAL((Class extends TabListHandler>) Global.class), GLOBAL_PING((Class extends TabListHandler>) GlobalPing.class), SERVER((Class extends TabListHandler>) ServerUnique.class);
+
+ private final Class extends TabListHandler> clazz;
+
+ private DefaultTabList(final Class extends TabListHandler> clazz) {
+ this.clazz = clazz;
+ }
+ }
+
+ public AuthServiceInfo getAuthSettings() {
+ final Map auth = this.get("authservice", new HashMap());
+ final List defaultJoinMessages = new ArrayList();
+ defaultJoinMessages.add("&3Welcome to my &aEaglercraftBungee &3server!");
+ return new AuthServiceInfo(this.get("enabled", false, auth), this.get("register_enabled", true, auth), this.get("authfile", "auths.db", auth),
+ this.get("ip_limit", 0, auth), this.get("join_messages", defaultJoinMessages, auth), this.get("login_timeout", 30, auth));
+ }
+
+ public Map getMap() {
+ return config;
+ }
+
+ public void forceSave() {
+ this.save();
+ }
+
+ public Collection getBlacklistURLs() {
+ boolean blacklistEnable = this.getBoolean("enable_web_origin_blacklist", true);
+ if(!blacklistEnable) {
+ return null;
+ }
+ Collection c = this.get("origin_blacklist_subscriptions", null);
+ if(c == null) {
+ c = new ArrayList();
+ c.add("https://g.lax1dude.net/eaglercraft/origin_blacklist.txt");
+ c.add("https://raw.githubusercontent.com/LAX1DUDE/eaglercraft/main/stable-download/origin_blacklist.txt");
+ c = this.get("origin_blacklist_subscriptions", c);
+ }else {
+ if(c.remove("https://g.eags.us/eaglercraft/origin_blacklist.txt")) {
+ c.add("https://g.lax1dude.net/eaglercraft/origin_blacklist.txt");
+ this.save();
+ BungeeCord.getInstance().getLogger().warning("Your origin blacklist has been patched to use g.lax1dude.net instead");
+ }
+ }
+ return c;
+ }
+
+ public Collection getBlacklistSimpleWhitelist() {
+ Collection c = this.get("origin_blacklist_simple_whitelist", null);
+ if(c == null) {
+ c = new ArrayList();
+ c.add("type the name of your client's domain here");
+ c.add("(if 'origin_blacklist_use_simple_whitelist' is true)");
+ c.add("g.lax1dude.net");
+ c = this.get("origin_blacklist_simple_whitelist", c);
+ }
+ return c;
+ }
+
+ public Collection getDisabledCommands() {
+ return this.get("disabled_commands", new ArrayList());
+ }
+
+ public Collection getICEServers() {
+ Collection ret = new ArrayList();
+
+ Collection c = this.get("voice_stun_servers", null);
+ if(c == null) {
+ c = new ArrayList();
+ c.add("stun:stun.l.google.com:19302");
+ c.add("stun:stun1.l.google.com:19302");
+ c.add("stun:stun2.l.google.com:19302");
+ c.add("stun:stun3.l.google.com:19302");
+ c.add("stun:stun4.l.google.com:19302");
+ c.add("stun:openrelay.metered.ca:80");
+ c = this.get("voice_stun_servers", c);
+ }
+
+ ret.addAll(c);
+
+ Map turnServerList = this.get("voice_turn_servers", null);
+ if(turnServerList == null) {
+ turnServerList = new HashMap();
+ HashMap n = new HashMap();
+ n.put("url", "turn:openrelay.metered.ca:80");
+ n.put("username", "openrelayproject");
+ n.put("password", "openrelayproject");
+ turnServerList.put("openrelay1", n);
+
+ n = new HashMap();
+ n.put("url", "turn:openrelay.metered.ca:443");
+ n.put("username", "openrelayproject");
+ n.put("password", "openrelayproject");
+ turnServerList.put("openrelay2", n);
+
+ n = new HashMap();
+ n.put("url", "turn:openrelay.metered.ca:443?transport=tcp");
+ n.put("username", "openrelayproject");
+ n.put("password", "openrelayproject");
+ turnServerList.put("openrelay3", n);
+ turnServerList = this.get("voice_turn_servers", turnServerList);
+ }
+
+ for(Entry trn : turnServerList.entrySet()) {
+ Object o = trn.getValue();
+ if(o instanceof Map) {
+ Map o2 = (Map) o;
+ ret.add("" + o2.get("url") + ";" + o2.get("username") + ";" + o2.get("password"));
+ }
+ }
+
+ return ret;
+ }
+
+ //@Override
+ public Collection> getList(String path, Collection> def)
+ {
+ return this.get(path, def);
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/connection/CancelSendSignal.java b/eaglerbungee/src/main/java/net/md_5/bungee/connection/CancelSendSignal.java
new file mode 100644
index 0000000..5afaac2
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/connection/CancelSendSignal.java
@@ -0,0 +1,17 @@
+package net.md_5.bungee.connection;
+
+public class CancelSendSignal extends Error
+{
+
+ @Override
+ public Throwable initCause(Throwable cause)
+ {
+ return this;
+ }
+
+ @Override
+ public Throwable fillInStackTrace()
+ {
+ return this;
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java b/eaglerbungee/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java
new file mode 100644
index 0000000..e668f1c
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java
@@ -0,0 +1,344 @@
+package net.md_5.bungee.connection;
+
+import com.google.common.io.ByteArrayDataInput;
+import com.google.common.io.ByteArrayDataOutput;
+import com.google.common.io.ByteStreams;
+
+import java.beans.ConstructorProperties;
+import java.io.DataInput;
+import java.net.InetAddress;
+import java.util.Objects;
+
+import net.md_5.bungee.*;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.config.ServerInfo;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.event.PluginMessageEvent;
+import net.md_5.bungee.api.event.ServerKickEvent;
+import net.md_5.bungee.api.score.Objective;
+import net.md_5.bungee.api.score.Position;
+import net.md_5.bungee.api.score.Score;
+import net.md_5.bungee.api.score.Scoreboard;
+import net.md_5.bungee.api.score.Team;
+import net.md_5.bungee.netty.ChannelWrapper;
+import net.md_5.bungee.netty.PacketHandler;
+import net.md_5.bungee.netty.PacketWrapper;
+import net.md_5.bungee.protocol.packet.Packet0KeepAlive;
+import net.md_5.bungee.protocol.packet.PacketC9PlayerListItem;
+import net.md_5.bungee.protocol.packet.PacketCEScoreboardObjective;
+import net.md_5.bungee.protocol.packet.PacketCFScoreboardScore;
+import net.md_5.bungee.protocol.packet.PacketD0DisplayScoreboard;
+import net.md_5.bungee.protocol.packet.PacketD1Team;
+import net.md_5.bungee.protocol.packet.PacketFAPluginMessage;
+import net.md_5.bungee.protocol.packet.PacketFFKick;
+
+public class DownstreamBridge extends PacketHandler
+{
+
+ private final ProxyServer bungee;
+ private final UserConnection con;
+ private final ServerConnection server;
+
+ @Override
+ public void exception(Throwable t) throws Exception
+ {
+ ServerInfo def = bungee.getServerInfo( con.getPendingConnection().getListener().getFallbackServer() );
+ if ( server.getInfo() != def )
+ {
+ server.setObsolete( true );
+ con.connectNow( def );
+ con.sendMessage( bungee.getTranslation( "server_went_down" ) );
+ } else
+ {
+ con.disconnect( Util.exception( t ) );
+ }
+ }
+
+ @Override
+ public void disconnected(ChannelWrapper channel) throws Exception
+ {
+ // We lost connection to the server
+ server.getInfo().removePlayer( con );
+ if ( bungee.getReconnectHandler() != null )
+ {
+ bungee.getReconnectHandler().setServer( con );
+ }
+
+ if ( !server.isObsolete() )
+ {
+ con.disconnect( bungee.getTranslation( "lost_connection" ) );
+ }
+ }
+
+ @Override
+ public void handle(PacketWrapper packet) throws Exception
+ {
+ if ( !server.isObsolete() )
+ {
+ EntityMap.rewrite( packet.buf, con.getServerEntityId(), con.getClientEntityId() );
+ con.sendPacket( packet );
+ }
+ }
+
+ @Override
+ public void handle(Packet0KeepAlive alive) throws Exception
+ {
+ con.setSentPingId( alive.getRandomId() );
+ con.setSentPingTime( System.currentTimeMillis() );
+ }
+
+ @Override
+ public void handle(PacketC9PlayerListItem playerList) throws Exception
+ {
+
+ if ( !con.getTabList().onListUpdate( playerList.getUsername(), playerList.isOnline(), playerList.getPing() ) )
+ {
+ throw new CancelSendSignal();
+ }
+ }
+
+ @Override
+ public void handle(PacketCEScoreboardObjective objective) throws Exception
+ {
+ Scoreboard serverScoreboard = con.getServerSentScoreboard();
+ switch ( objective.getAction() )
+ {
+ case 0:
+ serverScoreboard.addObjective( new Objective( objective.getName(), objective.getText() ) );
+ break;
+ case 1:
+ serverScoreboard.removeObjective( objective.getName() );
+ break;
+ }
+ }
+
+ @Override
+ public void handle(PacketCFScoreboardScore score) throws Exception
+ {
+ Scoreboard serverScoreboard = con.getServerSentScoreboard();
+ switch ( score.getAction() )
+ {
+ case 0:
+ Score s = new Score( score.getItemName(), score.getScoreName(), score.getValue() );
+ serverScoreboard.removeScore( score.getItemName() );
+ serverScoreboard.addScore( s );
+ break;
+ case 1:
+ serverScoreboard.removeScore( score.getItemName() );
+ break;
+ }
+ }
+
+ @Override
+ public void handle(PacketD0DisplayScoreboard displayScoreboard) throws Exception
+ {
+ Scoreboard serverScoreboard = con.getServerSentScoreboard();
+ serverScoreboard.setName( displayScoreboard.getName() );
+ serverScoreboard.setPosition( Position.values()[displayScoreboard.getPosition()] );
+ }
+
+ @Override
+ public void handle(PacketD1Team team) throws Exception
+ {
+ Scoreboard serverScoreboard = con.getServerSentScoreboard();
+ // Remove team and move on
+ if ( team.getMode() == 1 )
+ {
+ serverScoreboard.removeTeam( team.getName() );
+ return;
+ }
+
+ // Create or get old team
+ Team t;
+ if ( team.getMode() == 0 )
+ {
+ t = new Team( team.getName() );
+ serverScoreboard.addTeam( t );
+ } else
+ {
+ t = serverScoreboard.getTeam( team.getName() );
+ }
+
+ if ( t != null )
+ {
+ if ( team.getMode() == 0 || team.getMode() == 2 )
+ {
+ t.setDisplayName( team.getDisplayName() );
+ t.setPrefix( team.getPrefix() );
+ t.setSuffix( team.getSuffix() );
+ t.setFriendlyFire( team.isFriendlyFire() );
+ }
+ if ( team.getPlayers() != null )
+ {
+ for ( String s : team.getPlayers() )
+ {
+ if ( team.getMode() == 0 || team.getMode() == 3 )
+ {
+ t.addPlayer( s );
+ } else
+ {
+ t.removePlayer( s );
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void handle(final PacketFAPluginMessage pluginMessage) throws Exception {
+ final ByteArrayDataInput in = ByteStreams.newDataInput(pluginMessage.getData());
+ final PluginMessageEvent event = new PluginMessageEvent(this.con.getServer(), this.con, pluginMessage.getTag(), pluginMessage.getData().clone());
+ if (this.bungee.getPluginManager().callEvent(event).isCancelled()) {
+ throw new CancelSendSignal();
+ }
+ if (pluginMessage.getTag().equals("MC|TPack") && this.con.getPendingConnection().getListener().getTexturePack() != null) {
+ throw new CancelSendSignal();
+ }
+ if (pluginMessage.getTag().equals("BungeeCord")) {
+ ByteArrayDataOutput out = ByteStreams.newDataOutput();
+ final String subChannel = in.readUTF();
+ if (subChannel.equals("Forward")) {
+ final String target = in.readUTF();
+ final String channel = in.readUTF();
+ final short len = in.readShort();
+ final byte[] data = new byte[len];
+ in.readFully(data);
+ out.writeUTF(channel);
+ out.writeShort(data.length);
+ out.write(data);
+ final byte[] payload = out.toByteArray();
+ out = null;
+ if (target.equals("ALL")) {
+ for (final ServerInfo server : this.bungee.getServers().values()) {
+ if (server != this.con.getServer().getInfo()) {
+ server.sendData("BungeeCord", payload);
+ }
+ }
+ } else {
+ final ServerInfo server2 = this.bungee.getServerInfo(target);
+ if (server2 != null) {
+ server2.sendData("BungeeCord", payload);
+ }
+ }
+ }
+ if (subChannel.equals("Connect")) {
+ final ServerInfo server3 = this.bungee.getServerInfo(in.readUTF());
+ if (server3 != null) {
+ this.con.connect(server3);
+ }
+ }
+ if (subChannel.equals("IP")) {
+ out.writeUTF("IP");
+ Object ob = this.con.getAttachment().get("remoteAddr");
+ if(ob != null && (ob instanceof InetAddress)) {
+ out.writeUTF(((InetAddress)ob).getHostAddress());
+ out.writeInt(this.con.getAddress().getPort());
+ }else {
+ out.writeUTF(this.con.getAddress().getHostString());
+ out.writeInt(this.con.getAddress().getPort());
+ }
+ }
+ if (subChannel.equals("PlayerCount")) {
+ final String target = in.readUTF();
+ out.writeUTF("PlayerCount");
+ if (target.equals("ALL")) {
+ out.writeUTF("ALL");
+ out.writeInt(this.bungee.getOnlineCount());
+ } else {
+ final ServerInfo server4 = this.bungee.getServerInfo(target);
+ if (server4 != null) {
+ out.writeUTF(server4.getName());
+ out.writeInt(server4.getPlayers().size());
+ }
+ }
+ }
+ if (subChannel.equals("PlayerList")) {
+ final String target = in.readUTF();
+ out.writeUTF("PlayerList");
+ if (target.equals("ALL")) {
+ out.writeUTF("ALL");
+ out.writeUTF(Util.csv(this.bungee.getPlayers()));
+ } else {
+ final ServerInfo server4 = this.bungee.getServerInfo(target);
+ if (server4 != null) {
+ out.writeUTF(server4.getName());
+ out.writeUTF(Util.csv(server4.getPlayers()));
+ }
+ }
+ }
+ if (subChannel.equals("GetServers")) {
+ out.writeUTF("GetServers");
+ out.writeUTF(Util.csv(this.bungee.getServers().keySet()));
+ }
+ if (subChannel.equals("Message")) {
+ final ProxiedPlayer target2 = this.bungee.getPlayer(in.readUTF());
+ if (target2 != null) {
+ target2.sendMessage(in.readUTF());
+ }
+ }
+ if (subChannel.equals("GetServer")) {
+ out.writeUTF("GetServer");
+ out.writeUTF(this.server.getInfo().getName());
+ }
+ if (subChannel.equals("EAG|GetDomain")) {
+ out.writeUTF("EAG|GetDomain");
+ Object ob = this.con.getAttachment().get("origin");
+ if(ob != null && (ob instanceof String)) {
+ out.writeBoolean(true);
+ out.writeUTF((String)ob);
+ }else {
+ out.writeBoolean(false);
+ out.writeUTF("");
+ }
+ }
+ if (subChannel.equals("EAG|ConsoleCommand")) {
+ if(BungeeCord.getInstance().config.shouldAcceptBukkitConsoleCommandPacket()) {
+ String cmd = in.readUTF();
+ bungee.getLogger().info("Connection [" + this.con.getName() + "] <-> [" + this.server.getInfo().getName() + "] executed bungee console command: " + cmd);
+ bungee.getPluginManager().dispatchCommand(bungee.getConsole(), cmd);
+ }else {
+ bungee.getLogger().info("Connection [" + this.con.getName() + "] <-> [" + this.server.getInfo().getName() + "] tried executing a bungee console command but \"accept_bukkit_console_command_packets\" is set to false in config.yml");
+ }
+ }
+ if (out != null) {
+ final byte[] b = out.toByteArray();
+ if (b.length != 0) {
+ this.con.getServer().sendData("BungeeCord", b);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void handle(PacketFFKick kick) throws Exception
+ {
+ ServerInfo def = bungee.getServerInfo( con.getPendingConnection().getListener().getFallbackServer() );
+ if ( Objects.equals( server.getInfo(), def ) )
+ {
+ def = null;
+ }
+ ServerKickEvent event = bungee.getPluginManager().callEvent( new ServerKickEvent( con, kick.getMessage(), def, ServerKickEvent.State.CONNECTED ) );
+ if ( event.isCancelled() && event.getCancelServer() != null )
+ {
+ con.connectNow( event.getCancelServer() );
+ } else
+ {
+ con.disconnect( bungee.getTranslation( "server_kick" ) + event.getKickReason() );
+ }
+ server.setObsolete( true );
+ throw new CancelSendSignal();
+ }
+
+ @Override
+ public String toString()
+ {
+ return "[" + con.getName() + "] <-> DownstreamBridge <-> [" + server.getInfo().getName() + "]";
+ }
+
+ @ConstructorProperties({ "bungee", "con", "server" })
+ public DownstreamBridge(final ProxyServer bungee, final UserConnection con, final ServerConnection server) {
+ this.bungee = bungee;
+ this.con = con;
+ this.server = server;
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/eaglerbungee/src/main/java/net/md_5/bungee/connection/InitialHandler.java
new file mode 100644
index 0000000..647c5c3
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/connection/InitialHandler.java
@@ -0,0 +1,354 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.connection;
+
+import java.beans.ConstructorProperties;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.security.GeneralSecurityException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+
+import com.google.common.base.Preconditions;
+
+import io.netty.channel.ChannelHandler;
+import net.md_5.bungee.BungeeCord;
+import net.md_5.bungee.EncryptionUtil;
+import net.md_5.bungee.PacketConstants;
+import net.md_5.bungee.UserConnection;
+import net.md_5.bungee.Util;
+import net.md_5.bungee.api.Callback;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.ServerPing;
+import net.md_5.bungee.api.config.ListenerInfo;
+import net.md_5.bungee.api.config.ServerInfo;
+import net.md_5.bungee.api.connection.Connection;
+import net.md_5.bungee.api.connection.PendingConnection;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.event.LoginEvent;
+import net.md_5.bungee.api.event.PostLoginEvent;
+import net.md_5.bungee.api.event.ProxyPingEvent;
+import net.md_5.bungee.eaglercraft.AuthHandler;
+import net.md_5.bungee.eaglercraft.BanList;
+import net.md_5.bungee.eaglercraft.BanList.BanCheck;
+import net.md_5.bungee.eaglercraft.BanList.BanState;
+import net.md_5.bungee.eaglercraft.WebSocketProxy;
+import net.md_5.bungee.netty.ChannelWrapper;
+import net.md_5.bungee.netty.CipherDecoder;
+import net.md_5.bungee.netty.CipherEncoder;
+import net.md_5.bungee.netty.HandlerBoss;
+import net.md_5.bungee.netty.PacketDecoder;
+import net.md_5.bungee.netty.PacketHandler;
+import net.md_5.bungee.netty.PipelineUtils;
+import net.md_5.bungee.protocol.Forge;
+import net.md_5.bungee.protocol.packet.*;
+
+public class InitialHandler extends PacketHandler implements PendingConnection {
+ private final ProxyServer bungee;
+ private ChannelWrapper ch;
+ private final ListenerInfo listener;
+ private Packet1Login forgeLogin;
+ private Packet2Handshake handshake;
+ private PacketFDEncryptionRequest request;
+ private List loginMessages;
+ private List registerMessages;
+ private State thisState;
+ private SecretKey sharedKey;
+ private final Connection.Unsafe unsafe;
+
+ @Override
+ public void connected(final ChannelWrapper channel) throws Exception {
+ this.ch = channel;
+ }
+
+ @Override
+ public void exception(final Throwable t) throws Exception {
+ this.disconnect(ChatColor.RED + Util.exception(t));
+ }
+
+ @Override
+ public void handle(final PacketFAPluginMessage pluginMessage) throws Exception {
+ if (pluginMessage.getTag().equals("REGISTER")) {
+ this.registerMessages.add(pluginMessage);
+ } else {
+ this.loginMessages.add(pluginMessage);
+ }
+ }
+
+ @Override
+ public void handle(final PacketFEPing ping) throws Exception {
+ ServerPing response = new ServerPing(this.bungee.getProtocolVersion(), this.bungee.getGameVersion(), this.listener.getMotd(), this.bungee.getOnlineCount(), this.listener.getMaxPlayers());
+ response = this.bungee.getPluginManager().callEvent(new ProxyPingEvent(this, response)).getResponse();
+ final String kickMessage = ChatColor.DARK_BLUE + "\u0000" + response.getProtocolVersion() + "\u0000" + response.getGameVersion() + "\u0000" + response.getMotd() + "\u0000" + response.getCurrentPlayers() + "\u0000"
+ + response.getMaxPlayers();
+ this.disconnect(kickMessage);
+ }
+
+ @Override
+ public void handle(final Packet1Login login) throws Exception {
+ Preconditions.checkState(this.thisState == State.LOGIN, (Object) "Not expecting FORGE LOGIN");
+ Preconditions.checkState(this.forgeLogin == null, (Object) "Already received FORGE LOGIN");
+ this.forgeLogin = login;
+ ((PacketDecoder) this.ch.getHandle().pipeline().get((Class) PacketDecoder.class)).setProtocol(Forge.getInstance());
+ }
+
+ @Override
+ public void handle(final Packet2Handshake handshake) throws Exception {
+ Preconditions.checkState(this.thisState == State.HANDSHAKE, (Object) "Not expecting HANDSHAKE");
+ this.handshake = handshake;
+ this.bungee.getLogger().log(Level.INFO, "{0} has connected", this);
+ boolean skipEncryption = false;
+ if (handshake.getProcolVersion() == 69) {
+ skipEncryption = true;
+ this.handshake.swapProtocol((byte) 78);
+ }else if(handshake.getProcolVersion() > 78) {
+ this.disconnect("this server does not support microsoft accounts");
+ return;
+ }else if(handshake.getProcolVersion() != 78) {
+ this.disconnect("minecraft 1.5.2 required for eaglercraft backdoor access");
+ return;
+ }
+ String un = handshake.getUsername();
+ if (un.length() < 3) {
+ this.disconnect("Username must be at least 3 characters");
+ return;
+ }
+ if (un.length() > 16) {
+ this.disconnect("Cannot have username longer than 16 characters");
+ return;
+ }
+ if(!un.equals(un.replaceAll("[^A-Za-z0-9\\-_]", "_").trim())) {
+ this.disconnect("Go fuck yourself");
+ return;
+ }
+ if (BungeeCord.getInstance().tokenVerify.isEmpty()) {
+ String hostname = handshake.getHost();
+ if (hostname.contains(":")) {
+ handshake.setHost(hostname.substring(0, hostname.indexOf(':')));
+ }
+ } else {
+ handshake.setHost(BungeeCord.getInstance().tokenVerify);
+ handshake.setPort(0);
+ }
+ InetAddress sc;
+ synchronized(WebSocketProxy.localToRemote) {
+ sc = WebSocketProxy.localToRemote.get(this.ch.getHandle().remoteAddress());
+ }
+ if(sc == null) {
+ this.bungee.getLogger().log(Level.WARNING, "player '" + un + "' doesn't have a websocket IP, remote address: " + this.ch.getHandle().remoteAddress().toString());
+ }else {
+ BanCheck bc = BanList.checkIpBanned(sc);
+ if(bc.isBanned()) {
+ this.bungee.getLogger().log(Level.SEVERE, "Player '" + un + "' [" + sc.toString() + "] is banned by IP: " + bc.match + " (" + bc.string + ")");
+ this.disconnect("" + ChatColor.RED + "You are banned.\n" + ChatColor.DARK_GRAY + "Reason: " + bc.string);
+ return;
+ }else {
+ this.bungee.getLogger().log(Level.INFO, "Player '" + un + "' [" + sc.toString() + "] has remote websocket IP: " + sc.getHostAddress());
+ }
+ }
+ String dnm;
+ synchronized(WebSocketProxy.localToRemote) {
+ dnm = WebSocketProxy.origins.get(this.ch.getHandle().remoteAddress());
+ }
+ if(dnm != null) {
+ if(dnm.equalsIgnoreCase("null")) {
+ this.bungee.getLogger().log(Level.INFO, "Player '" + un + "' [" + sc.toString() + "] is using an offline download");
+ }else {
+ this.bungee.getLogger().log(Level.INFO, "Player '" + un + "' [" + sc.toString() + "] is using a client at: " + dnm);
+ }
+ }
+ BanCheck bc = BanList.checkBanned(un);
+ if(bc.isBanned()) {
+ switch(bc.reason) {
+ case USER_BANNED:
+ this.bungee.getLogger().log(Level.SEVERE, "Player '" + un + "' is banned by username, because '" + bc.string + "'");
+ break;
+ case WILDCARD_BANNED:
+ this.bungee.getLogger().log(Level.SEVERE, "Player '" + un + "' is banned by wildcard: " + bc.match);
+ break;
+ case REGEX_BANNED:
+ this.bungee.getLogger().log(Level.SEVERE, "Player '" + un + "' is banned by regex: " + bc.match);
+ break;
+ default:
+ this.bungee.getLogger().log(Level.SEVERE, "Player '" + un + "' is banned: " + bc.string);
+ }
+ if(bc.reason == BanState.USER_BANNED || ((BungeeCord)bungee).config.shouldShowBanType()) {
+ this.disconnect("" + ChatColor.RED + "You are banned.\n" + ChatColor.DARK_GRAY + "Reason: " + bc.string);
+ }else {
+ this.disconnect("" + ChatColor.RED + "You are banned.");
+ }
+ return;
+ }
+ final int limit = BungeeCord.getInstance().config.getPlayerLimit();
+ if (limit > 0 && this.bungee.getOnlineCount() > limit) {
+ this.disconnect(this.bungee.getTranslation("proxy_full"));
+ return;
+ }
+ if (!BungeeCord.getInstance().config.isOnlineMode() && this.bungee.getPlayer(un) != null) {
+ this.disconnect(this.bungee.getTranslation("already_connected"));
+ return;
+ }
+ this.unsafe().sendPacket(PacketConstants.I_AM_BUNGEE);
+ this.unsafe().sendPacket(PacketConstants.FORGE_MOD_REQUEST);
+ if(skipEncryption) {
+ InitialHandler.this.thisState = State.LOGIN;
+ handle((PacketCDClientStatus)null);
+ }else {
+ this.unsafe().sendPacket(this.request = EncryptionUtil.encryptRequest());
+ this.thisState = State.ENCRYPT;
+ }
+ }
+
+ @Override
+ public void handle(final PacketFCEncryptionResponse encryptResponse) throws Exception {
+ Preconditions.checkState(this.thisState == State.ENCRYPT, (Object) "Not expecting ENCRYPT");
+ this.sharedKey = EncryptionUtil.getSecret(encryptResponse, this.request);
+ final Cipher decrypt = EncryptionUtil.getCipher(2, this.sharedKey);
+ this.ch.getHandle().pipeline().addBefore(PipelineUtils.PACKET_DECODE_HANDLER, PipelineUtils.DECRYPT_HANDLER, (ChannelHandler)new CipherDecoder(decrypt));
+ this.finish();
+ }
+
+ private void finish() throws GeneralSecurityException {
+ final ProxiedPlayer old = this.bungee.getPlayer(this.handshake.getUsername());
+ if (old != null) {
+ old.disconnect(this.bungee.getTranslation("already_connected"));
+ }
+ final Callback complete = new Callback() {
+ @Override
+ public void done(final LoginEvent result, final Throwable error) {
+ if (result.isCancelled()) {
+ InitialHandler.this.disconnect(result.getCancelReason());
+ }
+ if (InitialHandler.this.ch.isClosed()) {
+ return;
+ }
+ InitialHandler.this.thisState = State.LOGIN;
+ InitialHandler.this.ch.getHandle().eventLoop().execute((Runnable)new Runnable() {
+ @Override
+ public void run() {
+ InitialHandler.this.unsafe().sendPacket(new PacketFCEncryptionResponse(new byte[0], new byte[0]));
+ try {
+ final Cipher encrypt = EncryptionUtil.getCipher(1, InitialHandler.this.sharedKey);
+ InitialHandler.this.ch.getHandle().pipeline().addBefore(PipelineUtils.DECRYPT_HANDLER, PipelineUtils.ENCRYPT_HANDLER, (ChannelHandler)new CipherEncoder(encrypt));
+ }
+ catch (GeneralSecurityException ex) {
+ InitialHandler.this.disconnect("Cipher error: " + Util.exception(ex));
+ }
+ }
+ });
+ }
+ };
+ this.bungee.getPluginManager().callEvent(new LoginEvent(this, complete));
+ }
+
+ @Override
+ public void handle(final PacketCDClientStatus clientStatus) throws Exception {
+ Preconditions.checkState(this.thisState == State.LOGIN, (Object) "Not expecting LOGIN");
+ final UserConnection userCon = new UserConnection(this.bungee, this.ch, this.getName(), this);
+ InetAddress ins = WebSocketProxy.localToRemote.get(this.ch.getHandle().remoteAddress());
+ if(ins != null) {
+ userCon.getAttachment().put("remoteAddr", ins);
+ }
+ String origin = WebSocketProxy.origins.get(this.ch.getHandle().remoteAddress());
+ if(origin != null) {
+ userCon.getAttachment().put("origin", origin);
+ }
+ userCon.init();
+ HandlerBoss handlerBoss = ((HandlerBoss) this.ch.getHandle().pipeline().get((Class) HandlerBoss.class));
+ if (BungeeCord.getInstance().config.getAuthInfo().isEnabled()) {
+ handlerBoss.setHandler(new AuthHandler(this.bungee, userCon, handlerBoss));
+ } else {
+ this.bungee.getPluginManager().callEvent(new PostLoginEvent(userCon));
+ handlerBoss.setHandler(new UpstreamBridge(this.bungee, userCon));
+ final ServerInfo server = this.bungee.getReconnectHandler().getServer(userCon);
+ userCon.connect(server, true);
+ }
+ this.thisState = State.FINISHED;
+ throw new CancelSendSignal();
+ }
+
+ @Override
+ public synchronized void disconnect(final String reason) {
+ if (!this.ch.isClosed()) {
+ this.unsafe().sendPacket(new PacketFFKick(reason));
+ this.ch.close();
+ }
+ }
+
+ @Override
+ public String getName() {
+ return (this.handshake == null) ? null : this.handshake.getUsername();
+ }
+
+ @Override
+ public byte getVersion() {
+ return (byte) ((this.handshake == null) ? -1 : this.handshake.getProcolVersion());
+ }
+
+ @Override
+ public InetSocketAddress getVirtualHost() {
+ return (this.handshake == null) ? null : new InetSocketAddress(this.handshake.getHost(), this.handshake.getPort() & 0xFFFF);
+ }
+
+ @Override
+ public InetSocketAddress getAddress() {
+ return (InetSocketAddress) this.ch.getHandle().remoteAddress();
+ }
+
+ @Override
+ public Connection.Unsafe unsafe() {
+ return this.unsafe;
+ }
+
+ @Override
+ public String toString() {
+ return "[" + ((this.getName() != null) ? this.getName() : this.getAddress()) + "] <-> InitialHandler";
+ }
+
+ @ConstructorProperties({ "bungee", "listener" })
+ public InitialHandler(final ProxyServer bungee, final ListenerInfo listener) {
+ this.loginMessages = new ArrayList();
+ this.registerMessages = new ArrayList();
+ this.thisState = State.HANDSHAKE;
+ this.unsafe = new Connection.Unsafe() {
+ @Override
+ public void sendPacket(final DefinedPacket packet) {
+ InitialHandler.this.ch.write(packet);
+ }
+ };
+ this.bungee = bungee;
+ this.listener = listener;
+ }
+
+ @Override
+ public ListenerInfo getListener() {
+ return this.listener;
+ }
+
+ public Packet1Login getForgeLogin() {
+ return this.forgeLogin;
+ }
+
+ public Packet2Handshake getHandshake() {
+ return this.handshake;
+ }
+
+ public List getLoginMessages() {
+ return this.loginMessages;
+ }
+
+ public List getRegisterMessages() {
+ return this.registerMessages;
+ }
+
+ private enum State {
+ HANDSHAKE, ENCRYPT, LOGIN, FINISHED;
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/connection/PingHandler.java b/eaglerbungee/src/main/java/net/md_5/bungee/connection/PingHandler.java
new file mode 100644
index 0000000..0f60f45
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/connection/PingHandler.java
@@ -0,0 +1,52 @@
+//
+// Decompiled by Procyon v0.5.36
+//
+
+package net.md_5.bungee.connection;
+
+import java.beans.ConstructorProperties;
+
+import net.md_5.bungee.api.Callback;
+import net.md_5.bungee.api.ServerPing;
+import net.md_5.bungee.api.config.ServerInfo;
+import net.md_5.bungee.netty.ChannelWrapper;
+import net.md_5.bungee.netty.PacketHandler;
+import net.md_5.bungee.protocol.packet.PacketFFKick;
+
+public class PingHandler extends PacketHandler {
+ private final ServerInfo target;
+ private final Callback callback;
+ private static final byte[] pingBuf;
+
+ @Override
+ public void connected(final ChannelWrapper channel) throws Exception {
+ channel.write(PingHandler.pingBuf);
+ }
+
+ @Override
+ public void exception(final Throwable t) throws Exception {
+ this.callback.done(null, t);
+ }
+
+ @Override
+ public void handle(final PacketFFKick kick) throws Exception {
+ final String[] split = kick.getMessage().split("\u0000");
+ final ServerPing ping = new ServerPing(Byte.parseByte(split[1]), split[2], split[3], Integer.parseInt(split[4]), Integer.parseInt(split[5]));
+ this.callback.done(ping, null);
+ }
+
+ @Override
+ public String toString() {
+ return "[Ping Handler] -> " + this.target.getName();
+ }
+
+ @ConstructorProperties({ "target", "callback" })
+ public PingHandler(final ServerInfo target, final Callback callback) {
+ this.target = target;
+ this.callback = callback;
+ }
+
+ static {
+ pingBuf = new byte[] { -2, 1 };
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java b/eaglerbungee/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java
new file mode 100644
index 0000000..d2e5afe
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java
@@ -0,0 +1,148 @@
+package net.md_5.bungee.connection;
+
+import net.md_5.bungee.BungeeCord;
+import net.md_5.bungee.EntityMap;
+import net.md_5.bungee.UserConnection;
+import net.md_5.bungee.Util;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.event.ChatEvent;
+import net.md_5.bungee.api.event.PlayerDisconnectEvent;
+import net.md_5.bungee.api.event.PluginMessageEvent;
+import net.md_5.bungee.netty.ChannelWrapper;
+import net.md_5.bungee.netty.PacketHandler;
+import net.md_5.bungee.netty.PacketWrapper;
+import net.md_5.bungee.protocol.packet.Packet0KeepAlive;
+import net.md_5.bungee.protocol.packet.Packet3Chat;
+import net.md_5.bungee.protocol.packet.PacketCBTabComplete;
+import net.md_5.bungee.protocol.packet.PacketCCSettings;
+import net.md_5.bungee.protocol.packet.PacketFAPluginMessage;
+import java.util.ArrayList;
+import java.util.List;
+
+public class UpstreamBridge extends PacketHandler
+{
+
+ private final ProxyServer bungee;
+ private final UserConnection con;
+
+ public UpstreamBridge(ProxyServer bungee, UserConnection con)
+ {
+ this.bungee = bungee;
+ this.con = con;
+
+ BungeeCord.getInstance().addConnection( con );
+ con.getTabList().onConnect();
+ con.unsafe().sendPacket( BungeeCord.getInstance().registerChannels() );
+ }
+
+ @Override
+ public void exception(Throwable t) throws Exception
+ {
+ con.disconnect( Util.exception( t ) );
+ }
+
+ @Override
+ public void disconnected(ChannelWrapper channel) throws Exception
+ {
+ // We lost connection to the client
+ PlayerDisconnectEvent event = new PlayerDisconnectEvent( con );
+ bungee.getPluginManager().callEvent( event );
+ con.getTabList().onDisconnect();
+ BungeeCord.getInstance().removeConnection( con );
+
+ if ( con.getServer() != null )
+ {
+ con.getServer().disconnect( "Quitting" );
+ }
+ }
+
+ @Override
+ public void handle(PacketWrapper packet) throws Exception
+ {
+ EntityMap.rewrite( packet.buf, con.getClientEntityId(), con.getServerEntityId() );
+ if ( con.getServer() != null )
+ {
+ con.getServer().getCh().write( packet );
+ }
+ }
+
+ @Override
+ public void handle(Packet0KeepAlive alive) throws Exception
+ {
+ if ( alive.getRandomId() == con.getSentPingId() )
+ {
+ int newPing = (int) ( System.currentTimeMillis() - con.getSentPingTime() );
+ con.getTabList().onPingChange( newPing );
+ con.setPing( newPing );
+ }
+ }
+
+ @Override
+ public void handle(Packet3Chat chat) throws Exception
+ {
+ ChatEvent chatEvent = new ChatEvent( con, con.getServer(), chat.getMessage() );
+ if ( !bungee.getPluginManager().callEvent( chatEvent ).isCancelled() )
+ {
+ chat.setMessage( chatEvent.getMessage() );
+ if ( !chatEvent.isCommand() || !bungee.getPluginManager().dispatchCommand( con, chat.getMessage().substring( 1 ) ) )
+ {
+ con.getServer().unsafe().sendPacket( chat );
+ }
+ }
+ throw new CancelSendSignal();
+ }
+
+ @Override
+ public void handle(PacketCBTabComplete tabComplete) throws Exception
+ {
+ if ( tabComplete.getCursor().startsWith( "/" ) )
+ {
+ List results = new ArrayList<>();
+ bungee.getPluginManager().dispatchCommand( con, tabComplete.getCursor().substring( 1 ), results );
+
+ if ( !results.isEmpty() )
+ {
+ con.unsafe().sendPacket( new PacketCBTabComplete( results.toArray( new String[ results.size() ] ) ) );
+ throw new CancelSendSignal();
+ }
+ }
+ }
+
+ @Override
+ public void handle(PacketCCSettings settings) throws Exception
+ {
+ con.setSettings( settings );
+ }
+
+ @Override
+ public void handle(PacketFAPluginMessage pluginMessage) throws Exception
+ {
+ if ( pluginMessage.getTag().equals( "BungeeCord" ) )
+ {
+ throw new CancelSendSignal();
+ }
+ // Hack around Forge race conditions
+ if ( pluginMessage.getTag().equals( "FML" ) && pluginMessage.getStream().readUnsignedByte() == 1 )
+ {
+ throw new CancelSendSignal();
+ }
+
+ PluginMessageEvent event = new PluginMessageEvent( con, con.getServer(), pluginMessage.getTag(), pluginMessage.getData().clone() );
+ if ( bungee.getPluginManager().callEvent( event ).isCancelled() )
+ {
+ throw new CancelSendSignal();
+ }
+
+ // TODO: Unregister as well?
+ if ( pluginMessage.getTag().equals( "REGISTER" ) )
+ {
+ con.getPendingConnection().getRegisterMessages().add( pluginMessage );
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return "[" + con.getName() + "] -> UpstreamBridge";
+ }
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthHandler.java b/eaglerbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthHandler.java
new file mode 100644
index 0000000..f86f541
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthHandler.java
@@ -0,0 +1,206 @@
+package net.md_5.bungee.eaglercraft;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import net.md_5.bungee.BungeeCord;
+import net.md_5.bungee.ServerConnection;
+import net.md_5.bungee.UserConnection;
+import net.md_5.bungee.Util;
+import net.md_5.bungee.api.ProxyServer;
+import net.md_5.bungee.api.config.ServerInfo;
+import net.md_5.bungee.api.event.PostLoginEvent;
+import net.md_5.bungee.connection.CancelSendSignal;
+import net.md_5.bungee.connection.UpstreamBridge;
+import net.md_5.bungee.netty.ChannelWrapper;
+import net.md_5.bungee.netty.HandlerBoss;
+import net.md_5.bungee.netty.PacketHandler;
+import net.md_5.bungee.protocol.packet.Packet1Login;
+import net.md_5.bungee.protocol.packet.Packet9Respawn;
+import net.md_5.bungee.protocol.packet.Packet0DPositionAndLook;
+import net.md_5.bungee.protocol.packet.Packet3Chat;
+import net.md_5.bungee.protocol.packet.Packet0KeepAlive;
+import net.md_5.bungee.protocol.packet.PacketCCSettings;
+import net.md_5.bungee.protocol.packet.PacketFAPluginMessage;
+import net.md_5.bungee.protocol.packet.PacketFEPing;
+
+public class AuthHandler extends PacketHandler {
+ private static final AuthSystem authSystem = BungeeCord.getInstance().authSystem;
+
+ private final ProxyServer bungee;
+ private final UserConnection con;
+ private final HandlerBoss handlerBoss;
+ private final String username;
+
+ private static final Collection openHandlers = new LinkedList();
+ private boolean loggedIn = false;
+ private long startTime;
+
+ public AuthHandler(final ProxyServer bungee, final UserConnection con, final HandlerBoss handlerBoss) {
+ this.bungee = bungee;
+ this.con = con;
+ this.handlerBoss = handlerBoss;
+ this.username = this.con.getName();
+ this.startTime = System.currentTimeMillis();
+
+ synchronized(openHandlers) {
+ openHandlers.add(this);
+ }
+
+ this.con.unsafe().sendPacket(new Packet1Login(0, "END", (byte) 2, 1, (byte) 0, (byte) 0,
+ (byte) this.con.getPendingConnection().getListener().getTabListSize()));
+ this.con.unsafe().sendPacket(new Packet9Respawn(1, (byte) 0, (byte) 2, (short) 255, "END"));
+ this.con.unsafe().sendPacket(new Packet0DPositionAndLook(0, 0, 0, 0, 0f, 0f, true));
+
+ this.con.sendMessages(authSystem.joinMessages);
+
+ if (authSystem.isRegistered(this.username)) {
+ this.con.sendMessage("\u00A7cPlease login to continue! /login ");
+ } else {
+ this.con.sendMessage("\u00A7cPlease register to continue! /register ");
+ }
+ }
+
+ @Override
+ public void exception(final Throwable t) throws Exception {
+ this.con.disconnect(Util.exception(t));
+ }
+
+ @Override
+ public void disconnected(final ChannelWrapper channel) {
+ this.loggedIn = true;
+ }
+
+ @Override
+ public void handle(final Packet0KeepAlive alive) throws Exception {
+ if (alive.getRandomId() == this.con.getSentPingId()) {
+ final int newPing = (int) (System.currentTimeMillis() - this.con.getSentPingTime());
+ this.con.setPing(newPing);
+ }
+ }
+
+ private List pms = new ArrayList<>();
+
+ @Override
+ public void handle(final PacketFAPluginMessage p) throws Exception {
+ pms.add(p);
+ throw new CancelSendSignal();
+ }
+
+ @Override
+ public void handle(final PacketFEPing p) throws Exception {
+ this.con.getPendingConnection().handle(p);
+ }
+
+ @Override
+ public void handle(final Packet3Chat chat) throws Exception {
+ String message = chat.getMessage();
+ if (message.startsWith("/")) {
+ String[] args = message.substring(1).trim().split(" ");
+ switch (args[0]) {
+ case "login":
+ case "l":
+ if (args.length == 1) {
+ this.con.sendMessage("\u00A7cYou must specify a password to login! /login ");
+ } else if (!authSystem.isRegistered(this.username)) {
+ this.con.sendMessage("\u00A7cThis username is not registered on this server!");
+ } else if (authSystem.login(this.username, args[1])) {
+ this.con.sendMessage("\u00A7cLogging in...");
+ this.onLogin();
+ } else {
+ this.con.sendMessage("\u00A7cThat password is invalid!");
+ }
+ break;
+ case "register":
+ case "reg":
+ if(BungeeCord.getInstance().config.getAuthInfo().isRegisterEnabled()) {
+ if (args.length == 1 || args.length == 2) {
+ this.con.sendMessage("\u00A7cUsage: /" + args[0].toLowerCase() + " ");
+ } else if (!args[1].equals(args[2])) {
+ this.con.sendMessage("\u00A7cThose passwords do not match!");
+ } else if (authSystem.isRegistered(this.username)) {
+ this.con.sendMessage("\u00A7cThis username is already registered!");
+ } else if (authSystem.register(this.username, args[1],
+ this.con.getAddress().getAddress().getHostAddress())) {
+ this.con.sendMessage("\u00A7cSuccessfully registered and logging in...");
+ this.onLogin();
+ } else {
+ this.con.sendMessage("\u00A7cUnable to register...");
+ }
+ }else {
+ this.con.disconnect("Registration is not enabled!");
+ }
+ break;
+ case "changepassword":
+ case "changepasswd":
+ case "changepwd":
+ case "changepass":
+ if (args.length == 1 || args.length == 2) {
+ this.con.sendMessage("\u00A7cUsage: /" + args[0].toLowerCase() + " ");
+ } else if (authSystem.login(this.username, args[1])) {
+ if (authSystem.changePass(this.username, args[2])) {
+ this.con.sendMessage("\u00A7cPassword changed successfully!");
+ } else {
+ this.con.sendMessage("\u00A7cUnable to change your password...");
+ }
+ } else {
+ this.con.sendMessage("\u00A7cThe old password specified is incorrect!");
+ }
+ break;
+ default:
+ }
+ }
+ }
+
+ private void onLogin() throws Exception {
+ this.loggedIn = true;
+ this.bungee.getPluginManager().callEvent(new PostLoginEvent(this.con));
+ UpstreamBridge ub = new UpstreamBridge(this.bungee, this.con);
+ handlerBoss.setHandler(ub);
+ final ServerInfo server = this.bungee.getReconnectHandler().getServer(this.con);
+ this.con.setServer(new ServerConnection(null, null));
+ this.con.connect(server, true);
+ for (PacketFAPluginMessage pm : pms) {
+ try {
+ ub.handle(pm);
+ this.con.getPendingConnection().getLoginMessages().add(pm);
+ } catch (CancelSendSignal e) {
+ // don't forward to server
+ }
+ }
+ pms.clear();
+ }
+
+ @Override
+ public void handle(final PacketCCSettings settings) throws Exception {
+ this.con.setSettings(settings);
+ }
+
+ @Override
+ public String toString() {
+ return "[" + this.con.getName() + "] -> AuthHandler";
+ }
+
+ public static void closeInactive(int timeout) {
+ synchronized(openHandlers) {
+ long millis = System.currentTimeMillis();
+ timeout *= 1000;
+ Iterator handlers = openHandlers.iterator();
+ while(handlers.hasNext()) {
+ AuthHandler h = handlers.next();
+ if(!h.loggedIn) {
+ if(millis - h.startTime > timeout) {
+ h.con.disconnect("You did not login in time you eagler!");
+ handlers.remove();
+ }
+ }else {
+ handlers.remove();
+ }
+ }
+ }
+ }
+
+}
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthSystem.java b/eaglerbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthSystem.java
new file mode 100644
index 0000000..de2fc01
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/eaglercraft/AuthSystem.java
@@ -0,0 +1,206 @@
+package net.md_5.bungee.eaglercraft;
+
+import net.md_5.bungee.BungeeCord;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.config.AuthServiceInfo;
+
+import java.io.File;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class AuthSystem {
+ private final String authFileName;
+ private final int ipLimit;
+ public final String[] joinMessages;
+
+ public AuthSystem(AuthServiceInfo authInfo) {
+ this.authFileName = authInfo.getAuthfile();
+ this.ipLimit = authInfo.getIpLimit();
+ List listJoinMessages = authInfo.getJoinMessages();
+ String[] arrayJoinMessages = new String[listJoinMessages.size()];
+ for (int i = 0; i < listJoinMessages.size(); i++) {
+ arrayJoinMessages[i] = ChatColor.translateAlternateColorCodes('&', listJoinMessages.get(i));
+ }
+ this.joinMessages = arrayJoinMessages;
+
+ this.readDatabase();
+ }
+
+ private static class AuthData {
+ public String salt;
+ public String hash;
+ public String ip;
+ public long timestamp;
+
+ public AuthData(String salt, String hash, String ip, long timestamp) {
+ this.salt = salt;
+ this.hash = hash;
+ this.ip = ip;
+ this.timestamp = timestamp;
+ }
+ }
+
+ private final Map database = new HashMap<>();
+
+ public boolean register(String username, String password, String ip) {
+ username = username.toLowerCase();
+ synchronized (database) {
+ AuthData authData = database.get(username);
+ if (authData != null)
+ return false;
+ if (isIpAtTheLimit(ip))
+ return false;
+ String salt = createSalt(16);
+ String hash = getSaltedHash(password, salt);
+ database.put(username, new AuthData(salt, hash, ip, System.currentTimeMillis()));
+ writeDatabase();
+ return true;
+ }
+ }
+
+ public boolean isRegistered(String username) {
+ username = username.toLowerCase();
+ synchronized (database) {
+ return database.containsKey(username);
+ }
+ }
+
+ public boolean changePass(String username, String password) {
+ username = username.toLowerCase();
+ synchronized (database) {
+ AuthData authData = database.get(username);
+ authData.salt = createSalt(16);
+ authData.hash = getSaltedHash(password, authData.salt);
+ writeDatabase();
+ return true;
+ }
+ }
+
+ public boolean login(String username, String password) {
+ username = username.toLowerCase();
+ synchronized (database) {
+ AuthData authData = database.get(username);
+ if (authData == null)
+ return false;
+ return authData.hash.equals(getSaltedHash(password, authData.salt));
+ }
+ }
+
+ private boolean isIpAtTheLimit(String ip) {
+ synchronized (database) {
+ if (this.ipLimit <= 0)
+ return false;
+ int num = 0;
+ for (AuthData authData : database.values()) {
+ if (authData.ip.equals(ip))
+ num++;
+ if (num >= this.ipLimit) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ // only use once, on load
+ public void readDatabase() {
+ synchronized (database) {
+ try {
+ File authFile = new File(this.authFileName);
+ if (!authFile.exists())
+ authFile.createNewFile();
+
+ database.clear();
+
+ String[] lines = new String(Files.readAllBytes(authFile.toPath())).trim().split("\n");
+ if (lines.length == 1 && lines[0].isEmpty())
+ return;
+ boolean alreadyLogged = false;
+ for (String line : lines) {
+ String[] pieces = line.split(":");
+ if (!pieces[1].startsWith("$SHA$")) {
+ if (!alreadyLogged) {
+ alreadyLogged = true;
+ BungeeCord.getInstance().getLogger().warning(
+ "One or more entries in the auth file are hashed in an unsupported format! (not SHA-256!)");
+ }
+ // continue;
+ }
+ String[] saltHash = pieces[1].substring(pieces[1].substring(1).indexOf('$') + 2).split("\\$");
+ database.put(pieces[0],
+ new AuthData(saltHash[0], saltHash[1], pieces[2], Long.parseLong(pieces[3])));
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private void writeDatabase() {
+ synchronized (database) {
+ StringBuilder out = new StringBuilder();
+
+ for (String username : database.keySet()) {
+ AuthData entry = database.get(username);
+ out.append(username);
+ out.append(":$SHA$");
+ out.append(entry.salt);
+ out.append("$");
+ out.append(entry.hash);
+ out.append(":");
+ out.append(entry.ip);
+ out.append(":");
+ out.append(entry.timestamp);
+ out.append("\n");
+ }
+
+ try {
+ Files.write(Paths.get(this.authFileName), out.toString().getBytes());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ // hashing used is based on hashing from AuthMe
+
+ private static final SecureRandom rnd = new SecureRandom();
+
+ private static String getSHA256(String message) {
+ try {
+ MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
+ sha256.reset();
+ sha256.update(message.getBytes());
+ byte[] digest = sha256.digest();
+ return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
+ } catch (NoSuchAlgorithmException e) {
+ return "";
+ }
+ }
+
+ private static String getSaltedHash(String message, String salt) {
+ return getSHA256(getSHA256(message) + salt);
+ }
+
+ private static String createSalt(int length) {
+ try {
+ byte[] msg = new byte[40];
+ rnd.nextBytes(msg);
+ MessageDigest sha1 = MessageDigest.getInstance("SHA1");
+ sha1.reset();
+ byte[] digest = sha1.digest(msg);
+ return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest)).substring(0, length);
+ } catch (NoSuchAlgorithmException e) {
+ return "";
+ }
+ }
+}
\ No newline at end of file
diff --git a/eaglerbungee/src/main/java/net/md_5/bungee/eaglercraft/BanList.java b/eaglerbungee/src/main/java/net/md_5/bungee/eaglercraft/BanList.java
new file mode 100644
index 0000000..28b743d
--- /dev/null
+++ b/eaglerbungee/src/main/java/net/md_5/bungee/eaglercraft/BanList.java
@@ -0,0 +1,872 @@
+package net.md_5.bungee.eaglercraft;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.math.BigInteger;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import net.md_5.bungee.BungeeCord;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.eaglercraft.sun.net.util.IPAddressUtil;
+
+public class BanList {
+
+ private static final Object banListMutex = new Object();
+
+ public static enum BanState {
+ NOT_BANNED, USER_BANNED, IP_BANNED, WILDCARD_BANNED, REGEX_BANNED;
+ }
+
+ public static class BanCheck {
+ public final BanState reason;
+ public final String match;
+ public final String string;
+ private BanCheck(BanState reason, String match, String string) {
+ this.reason = reason;
+ this.match = match;
+ this.string = string;
+ }
+ public boolean isBanned() {
+ return reason != BanState.NOT_BANNED;
+ }
+ }
+
+ private static class RegexBan {
+ public final String string;
+ public final Pattern compiled;
+ private RegexBan(String string, Pattern compiled) {
+ this.string = string;
+ this.compiled = compiled;
+ }
+ public String toString() {
+ return string;
+ }
+ public int hashCode() {
+ return string.hashCode();
+ }
+ }
+
+ public static interface IPBan {
+
+ boolean checkBan(InetAddress addr);
+ InetAddress getBaseAddress();
+ boolean hasNetMask();
+
+ }
+
+ private static class IPBan4 implements IPBan {
+
+ private final int addr;
+ private final InetAddress addrI;
+ private final int mask;
+ private final String string;
+
+ protected IPBan4(Inet4Address addr, String s, int mask) {
+ if(mask >= 32) {
+ this.mask = 0xFFFFFFFF;
+ }else {
+ this.mask = ~((1 << (32 - mask)) - 1);
+ }
+ this.string = s;
+ byte[] bits = addr.getAddress();
+ this.addr = this.mask & ((bits[0] << 24) | (bits[1] << 16) | (bits[2] << 8) | (bits[3] & 0xFF));
+ this.addrI = addr;
+ }
+
+ @Override
+ public boolean checkBan(InetAddress addr4) {
+ if(addr4 instanceof Inet4Address) {
+ Inet4Address a = (Inet4Address)addr4;
+ byte[] bits = a.getAddress();
+ int addrBits = ((bits[0] << 24) | (bits[1] << 16) | (bits[2] << 8) | (bits[3] & 0xFF));
+ return (mask & addrBits) == addr;
+ }else {
+ return false;
+ }
+ }
+
+ @Override
+ public InetAddress getBaseAddress() {
+ return addrI;
+ }
+
+ @Override
+ public String toString() {
+ return string;
+ }
+
+ @Override
+ public int hashCode() {
+ return string.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o != null && o instanceof IPBan4 && ((IPBan4)o).addr == addr && ((IPBan4)o).mask == mask;
+ }
+
+ @Override
+ public boolean hasNetMask() {
+ return mask != 0xFFFFFFFF;
+ }
+
+ }
+
+ private static class IPBan6 implements IPBan {
+
+ private static final BigInteger mask128 = new BigInteger(1, new byte[] {
+ (byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,
+ (byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,
+ (byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,
+ (byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF
+ });
+
+ private final BigInteger addr;
+ private final InetAddress addrI;
+ private final BigInteger mask;
+ private final String string;
+
+ protected IPBan6(Inet6Address addr, String s, int mask) {
+ this.mask = BigInteger.valueOf(1l).shiftLeft(128 - mask).subtract(BigInteger.valueOf(1l)).xor(mask128);
+ this.string = s.toLowerCase();
+ this.addr = new BigInteger(1, addr.getAddress()).and(this.mask);
+ this.addrI = addr;
+ }
+
+ @Override
+ public boolean checkBan(InetAddress addr6) {
+ if(addr6 instanceof Inet6Address) {
+ Inet6Address a = (Inet6Address)addr6;
+ BigInteger addrBits = new BigInteger(1, a.getAddress()).and(this.mask);
+ return addr.equals(addrBits);
+ }else {
+ return false;
+ }
+ }
+
+ @Override
+ public InetAddress getBaseAddress() {
+ return addrI;
+ }
+
+ @Override
+ public String toString() {
+ return string;
+ }
+
+ @Override
+ public int hashCode() {
+ return string.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o != null && o instanceof IPBan6 && ((IPBan6)o).addr.equals(addr) && ((IPBan6)o).mask.equals(mask);
+ }
+
+ @Override
+ public boolean hasNetMask() {
+ return !mask.equals(mask128);
+ }
+
+ }
+
+ public static final File bansFile = new File("bans.txt");
+
+ public static final String banChatMessagePrefix = ChatColor.GOLD + "[BanList] ";
+
+ public static final Map userBans = new HashMap();
+ public static final Set ipBans = new HashSet();
+ public static final Set wildcardBans = new HashSet();
+ public static final Set