diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2751bdcb..93f1d1f5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+#####0.3
+
+* Added BungeeCord support
+* Decrease timeout checks in order to fail faster on connection problems
+* Code style improvements
+
######0.2.4
* Fixed NPE on invalid sessions
diff --git a/README.md b/README.md
index d0fa3cfb..04fe9e5f 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,10 @@ So they don't need to enter passwords. This is also called auto login.
* Automatically login paid accounts (premium)
* Support various of auth plugins
* Experimental Cauldron support
+* BungeeCord support
* No client modifications needed
+* Good performance by async non blocking operations
+* Free
***
@@ -114,16 +117,22 @@ and Mojang account. Then the command can be executed. So someone different canno
by buying the username.
####Does the plugin have BungeeCord support?
-Not yet, but I'm planning this.
+Yes it has. Just activate ipForward in your BungeeCord config and place the plugin in the plugins folder of
+Bukkit/Spigot and BungeeCord. This plugin will automatically detect if BungeeCord is running and so handle checks
+there.
####Could premium players have a premium UUID and Skin?
Something like that is possible, but is not yet implemented.
####Is this plugin compatible with Cauldron?
-It's not yet tested, but all needed methods also exists in Cauldron so it could work together
+It's not tested yet, but all needed methods also exists in Cauldron so it could work together.
***
###Useful Links:
* [Login Protocol](http://wiki.vg/Protocol#Login)
-* [Protocol Encryption](http://wiki.vg/Protocol_Encryption)
\ No newline at end of file
+* [Protocol Encryption](http://wiki.vg/Protocol_Encryption)
+
+###Donate
+
+[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=8ZBULMAPN7MZC)
\ No newline at end of file
diff --git a/bungee/pom.xml b/bungee/pom.xml
new file mode 100644
index 00000000..c067e4e0
--- /dev/null
+++ b/bungee/pom.xml
@@ -0,0 +1,99 @@
+
+ 4.0.0
+
+ com.github.games647
+
+ fastloginbungee
+ jar
+
+ FastLogin
+ 0.1
+ 2015
+ https://github.com/games647/FastLogin
+
+ Automatically logins premium (paid accounts) player on a offline mode server
+
+
+
+ UTF-8
+
+ ${basedir}/target
+
+
+
+ GitHub
+ https://github.com/games647/FastLogin/issues
+
+
+
+ https://github.com/games647/FastLogin
+ scm:git:git://github.com/games647/FastLogin.git
+ scm:git:ssh://git@github.com:games647/FastLogin.git
+
+
+
+ install
+
+ ${project.name}
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.2
+
+ 1.7
+ 1.7
+ true
+ true
+
+ false
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 2.6
+
+ ${outputDir}
+
+
+
+
+
+
+ src/main/resources
+
+ true
+
+
+
+
+ ${basedir}
+
+ LICENSE
+
+
+
+
+
+
+
+
+ bungeecord-repo
+ https://oss.sonatype.org/content/repositories/snapshots
+
+
+
+
+
+ net.md-5
+ bungeecord-api
+ 1.8-SNAPSHOT
+ jar
+ provided
+
+
+
diff --git a/bungee/src/main/java/com/github/games647/fastloginbungee/FastLogin.java b/bungee/src/main/java/com/github/games647/fastloginbungee/FastLogin.java
new file mode 100644
index 00000000..5384dd8b
--- /dev/null
+++ b/bungee/src/main/java/com/github/games647/fastloginbungee/FastLogin.java
@@ -0,0 +1,34 @@
+package com.github.games647.fastloginbungee;
+
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+import net.md_5.bungee.api.plugin.Plugin;
+
+/**
+ * BungeeCord version of FastLogin. This plugin keeps track
+ * on online mode connections.
+ */
+public class FastLogin extends Plugin {
+
+ private final Set enabledPremium = Sets.newConcurrentHashSet();
+
+ @Override
+ public void onEnable() {
+ //events
+ getProxy().getPluginManager().registerListener(this, new PlayerConnectionListener(this));
+
+ //commands
+ getProxy().getPluginManager().registerCommand(this, new PremiumCommand(this));
+ }
+
+ /**
+ * A set of players who want to use fastlogin
+ *
+ * @return all player which want to be in onlinemode
+ */
+ public Set getEnabledPremium() {
+ return enabledPremium;
+ }
+}
diff --git a/bungee/src/main/java/com/github/games647/fastloginbungee/PlayerConnectionListener.java b/bungee/src/main/java/com/github/games647/fastloginbungee/PlayerConnectionListener.java
new file mode 100644
index 00000000..ca77e532
--- /dev/null
+++ b/bungee/src/main/java/com/github/games647/fastloginbungee/PlayerConnectionListener.java
@@ -0,0 +1,78 @@
+package com.github.games647.fastloginbungee;
+
+import com.google.common.io.ByteArrayDataOutput;
+import com.google.common.io.ByteStreams;
+
+import java.util.UUID;
+
+import net.md_5.bungee.api.connection.PendingConnection;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.connection.Server;
+import net.md_5.bungee.api.event.PluginMessageEvent;
+import net.md_5.bungee.api.event.PreLoginEvent;
+import net.md_5.bungee.api.event.ServerConnectedEvent;
+import net.md_5.bungee.api.plugin.Listener;
+import net.md_5.bungee.event.EventHandler;
+
+/**
+ * Enables online mode logins for specified users and sends
+ * plugin message to the Bukkit version of this plugin in
+ * order to clear that the connection is online mode.
+ */
+public class PlayerConnectionListener implements Listener {
+
+ private final FastLogin plugin;
+
+ public PlayerConnectionListener(FastLogin plugin) {
+ this.plugin = plugin;
+ }
+
+ @EventHandler
+ public void onPreLogin(PreLoginEvent preLoginEvent) {
+ if (preLoginEvent.isCancelled()) {
+ return;
+ }
+
+ PendingConnection connection = preLoginEvent.getConnection();
+ String username = connection.getName();
+ //just enable it for activated users
+ if (plugin.getEnabledPremium().contains(username)) {
+ connection.setOnlineMode(true);
+ }
+ }
+
+ @EventHandler
+ public void onServerConnected(ServerConnectedEvent serverConnectedEvent) {
+ ProxiedPlayer player = serverConnectedEvent.getPlayer();
+ //send message even when the online mode is activated by default
+ if (player.getPendingConnection().isOnlineMode()) {
+ Server server = serverConnectedEvent.getServer();
+
+ ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
+ //subchannel name
+ dataOutput.writeUTF("Checked");
+
+ //proxy identifier to check if it's a acceptable proxy
+ UUID proxyId = UUID.fromString(plugin.getProxy().getConfig().getUuid());
+ dataOutput.writeLong(proxyId.getMostSignificantBits());
+ dataOutput.writeLong(proxyId.getLeastSignificantBits());
+
+ //Data is sent through a random player. We have to tell the Bukkit version of this plugin the target
+ dataOutput.writeUTF(player.getName());
+
+ server.sendData(plugin.getDescription().getName(), dataOutput.toByteArray());
+ }
+ }
+
+ @EventHandler
+ public void onPluginMessage(PluginMessageEvent pluginMessageEvent) {
+ String channel = pluginMessageEvent.getTag();
+ if (pluginMessageEvent.isCancelled() || !plugin.getDescription().getName().equals(channel)) {
+ return;
+ }
+
+ //the client shouldn't be able to read the messages in order to know something about server internal states
+ //moreover the client shouldn't be able fake a running premium check by sending the result message
+ pluginMessageEvent.setCancelled(true);
+ }
+}
diff --git a/bungee/src/main/java/com/github/games647/fastloginbungee/PremiumCommand.java b/bungee/src/main/java/com/github/games647/fastloginbungee/PremiumCommand.java
new file mode 100644
index 00000000..3497a09c
--- /dev/null
+++ b/bungee/src/main/java/com/github/games647/fastloginbungee/PremiumCommand.java
@@ -0,0 +1,38 @@
+package com.github.games647.fastloginbungee;
+
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.CommandSender;
+import net.md_5.bungee.api.chat.ComponentBuilder;
+import net.md_5.bungee.api.connection.ProxiedPlayer;
+import net.md_5.bungee.api.plugin.Command;
+
+/**
+ * Let players activate the fastlogin method on a BungeeCord instance.
+ */
+public class PremiumCommand extends Command {
+
+ private final FastLogin plugin;
+
+ public PremiumCommand(FastLogin plugin) {
+ super(plugin.getDescription().getName()
+ , plugin.getDescription().getName() + ".command." + "premium"
+ , "prem" , "premium", "loginfast");
+
+ this.plugin = plugin;
+ }
+
+ @Override
+ public void execute(CommandSender sender, String[] args) {
+ if (!(sender instanceof ProxiedPlayer)) {
+ sender.sendMessage(new ComponentBuilder("Only player can invoke this command")
+ .color(ChatColor.DARK_RED)
+ .create());
+ return;
+ }
+
+ plugin.getEnabledPremium().add(sender.getName());
+ sender.sendMessage(new ComponentBuilder("Added to the list of premium players")
+ .color(ChatColor.DARK_GREEN)
+ .create());
+ }
+}
diff --git a/bungee/src/main/resources/bungee.yml b/bungee/src/main/resources/bungee.yml
new file mode 100644
index 00000000..de451534
--- /dev/null
+++ b/bungee/src/main/resources/bungee.yml
@@ -0,0 +1,12 @@
+# project informations for BungeeCord
+# This file will be prioritised over plugin.yml which can be also used for Bungee
+# This make it easy to combine BungeeCord and Bukkit support in one plugin
+name: ${project.name}
+# ${...} will be automatically replaced by Maven
+main: ${project.groupId}.${project.artifactId}.${project.name}
+
+version: ${project.version}
+author: games647, http://github.com/games647/FastLogin/graphs/contributors
+
+description: |
+ ${project.description}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 0e46ce33..a8f2c59e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,7 +8,7 @@
jarFastLogin
- 0.2.4
+ 0.32015https://github.com/games647/FastLogin
diff --git a/src/main/java/com/github/games647/fastlogin/FastLogin.java b/src/main/java/com/github/games647/fastlogin/FastLogin.java
index d2c61c16..e304a924 100644
--- a/src/main/java/com/github/games647/fastlogin/FastLogin.java
+++ b/src/main/java/com/github/games647/fastlogin/FastLogin.java
@@ -1,13 +1,16 @@
package com.github.games647.fastlogin;
-import com.github.games647.fastlogin.listener.PlayerListener;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.utility.SafeCacheBuilder;
import com.github.games647.fastlogin.hooks.AuthPlugin;
+import com.github.games647.fastlogin.listener.BukkitJoinListener;
+import com.github.games647.fastlogin.listener.BungeeCordListener;
import com.github.games647.fastlogin.listener.EncryptionPacketListener;
+import com.github.games647.fastlogin.listener.HandshakePacketListener;
import com.github.games647.fastlogin.listener.StartPacketListener;
import com.google.common.cache.CacheLoader;
+import com.google.common.collect.MapMaker;
import com.google.common.collect.Sets;
import com.google.common.reflect.ClassPath;
@@ -20,16 +23,16 @@ import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
+import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
/**
- * This plugin checks if a player has a paid account and if so
- * tries to skip offline mode authentication.
+ * This plugin checks if a player has a paid account and if so tries to skip offline mode authentication.
*/
public class FastLogin extends JavaPlugin {
//http connection, read timeout and user agent for a connection to mojang api servers
- private static final int TIMEOUT = 10 * 1000;
+ private static final int TIMEOUT = 1 * 1000;
private static final String USER_AGENT = "Premium-Checker";
//provide a immutable key pair to be thread safe | used for encrypting and decrypting traffic
@@ -38,11 +41,14 @@ public class FastLogin extends JavaPlugin {
//we need a thread-safe set because we access it async in the packet listener
private final Set enabledPremium = Sets.newConcurrentHashSet();
+ //player=fake player created by Protocollib | this mapmaker creates a concurrent map with weak keys
+ private final ConcurrentMap bungeeCordUsers = new MapMaker().weakKeys().makeMap();
+
//this map is thread-safe for async access (Packet Listener)
//SafeCacheBuilder is used in order to be version independent
private final ConcurrentMap session = SafeCacheBuilder.newBuilder()
//2 minutes should be enough as a timeout for bad internet connection (Server, Client and Mojang)
- .expireAfterWrite(2, TimeUnit.MINUTES)
+ .expireAfterWrite(1, TimeUnit.MINUTES)
//mapped by ip:port -> PlayerSession
.build(new CacheLoader() {
@@ -72,11 +78,15 @@ public class FastLogin extends JavaPlugin {
//register packet listeners on success
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
- protocolManager.addPacketListener(new EncryptionPacketListener(this, protocolManager));
+ protocolManager.addPacketListener(new HandshakePacketListener(this));
protocolManager.addPacketListener(new StartPacketListener(this, protocolManager));
+ protocolManager.addPacketListener(new EncryptionPacketListener(this, protocolManager));
//register commands using a unique name
getCommand("premium").setExecutor(new PremiumCommand(this));
+
+ //check for incoming messages from the bungeecord version of this plugin
+ getServer().getMessenger().registerIncomingPluginChannel(this, this.getName(), new BungeeCordListener(this));
}
@Override
@@ -84,11 +94,12 @@ public class FastLogin extends JavaPlugin {
//clean up
session.clear();
enabledPremium.clear();
+ bungeeCordUsers.clear();
}
/**
- * Gets a thread-safe map about players which are connecting to the server are being
- * checked to be premium (paid account)
+ * Gets a thread-safe map about players which are connecting to the server
+ * are being checked to be premium (paid account)
*
* @return a thread-safe session map
*/
@@ -97,8 +108,20 @@ public class FastLogin extends JavaPlugin {
}
/**
- * Gets the server KeyPair. This is used to encrypt or decrypt traffic between
- * the client and server
+ * Gets a concurrent map with weak keys for all bungeecord users
+ * which could be detected. It's mapped by a fake instance of player
+ * created by Protocollib and a non-null raw object.
+ *
+ * Represents a similar set collection
+ *
+ * @return
+ */
+ public ConcurrentMap getBungeeCordUsers() {
+ return bungeeCordUsers;
+ }
+
+ /**
+ * Gets the server KeyPair. This is used to encrypt or decrypt traffic between the client and server
*
* @return the server KeyPair
*/
@@ -166,7 +189,7 @@ public class FastLogin extends JavaPlugin {
}
//We found a supporting plugin - we can now register a forwarding listener to skip authentication from them
- getServer().getPluginManager().registerEvents(new PlayerListener(this, authPluginHook), this);
+ getServer().getPluginManager().registerEvents(new BukkitJoinListener(this, authPluginHook), this);
return true;
}
}
diff --git a/src/main/java/com/github/games647/fastlogin/PlayerSession.java b/src/main/java/com/github/games647/fastlogin/PlayerSession.java
index eeee7d75..f76750f4 100644
--- a/src/main/java/com/github/games647/fastlogin/PlayerSession.java
+++ b/src/main/java/com/github/games647/fastlogin/PlayerSession.java
@@ -1,5 +1,7 @@
package com.github.games647.fastlogin;
+import org.apache.commons.lang.ArrayUtils;
+
/**
* Represents a client connecting to the server.
*
@@ -15,7 +17,7 @@ public class PlayerSession {
public PlayerSession(String username, String serverId, byte[] verifyToken) {
this.username = username;
this.serverId = serverId;
- this.verifyToken = verifyToken;
+ this.verifyToken = ArrayUtils.clone(verifyToken);
}
/**
@@ -37,7 +39,7 @@ public class PlayerSession {
* @return the verify token from the server
*/
public byte[] getVerifyToken() {
- return verifyToken;
+ return ArrayUtils.clone(verifyToken);
}
/**
diff --git a/src/main/java/com/github/games647/fastlogin/listener/BukkitJoinListener.java b/src/main/java/com/github/games647/fastlogin/listener/BukkitJoinListener.java
new file mode 100644
index 00000000..51956633
--- /dev/null
+++ b/src/main/java/com/github/games647/fastlogin/listener/BukkitJoinListener.java
@@ -0,0 +1,53 @@
+package com.github.games647.fastlogin.listener;
+
+import com.github.games647.fastlogin.FastLogin;
+import com.github.games647.fastlogin.PlayerSession;
+import com.github.games647.fastlogin.hooks.AuthPlugin;
+
+import java.util.logging.Level;
+
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerJoinEvent;
+
+/**
+ * This listener tells authentication plugins if the player has a premium account and we checked it successfully. So the
+ * plugin can skip authentication.
+ */
+public class BukkitJoinListener implements Listener {
+
+ private static final long DELAY_LOGIN = 2 * 20L;
+
+ protected final FastLogin plugin;
+ protected final AuthPlugin authPlugin;
+
+ public BukkitJoinListener(FastLogin plugin, AuthPlugin authPlugin) {
+ this.plugin = plugin;
+ this.authPlugin = authPlugin;
+ }
+
+ @EventHandler(ignoreCancelled = true)
+ public void onJoin(PlayerJoinEvent joinEvent) {
+ final Player player = joinEvent.getPlayer();
+
+ Bukkit.getScheduler().runTaskLater(plugin, new Runnable() {
+
+ @Override
+ public void run() {
+ String address = player.getAddress().toString();
+ //removing the session because we now use it
+ PlayerSession session = plugin.getSessions().remove(address);
+
+ //check if it's the same player as we checked before
+ if (player.isOnline() && session != null
+ && player.getName().equals(session.getUsername()) && session.isVerified()) {
+ plugin.getLogger().log(Level.FINE, "Logging player {0} in", player.getName());
+ authPlugin.forceLogin(player);
+ }
+ }
+ //Wait before auth plugin and we received a message from BungeeCord initializes the player
+ }, DELAY_LOGIN);
+ }
+}
diff --git a/src/main/java/com/github/games647/fastlogin/listener/BungeeCordListener.java b/src/main/java/com/github/games647/fastlogin/listener/BungeeCordListener.java
new file mode 100644
index 00000000..d738a2d0
--- /dev/null
+++ b/src/main/java/com/github/games647/fastlogin/listener/BungeeCordListener.java
@@ -0,0 +1,90 @@
+package com.github.games647.fastlogin.listener;
+
+import com.github.games647.fastlogin.FastLogin;
+import com.github.games647.fastlogin.PlayerSession;
+import com.google.common.base.Charsets;
+import com.google.common.io.ByteArrayDataInput;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Files;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.UUID;
+import java.util.logging.Level;
+
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.messaging.PluginMessageListener;
+
+/**
+ * Responsible for receiving messages from a BungeeCord instance.
+ *
+ * This class also receives the plugin message from the bungeecord version of this plugin in order to
+ * get notified if the connection is in online mode.
+ */
+public class BungeeCordListener implements PluginMessageListener {
+
+ private static final String FILE_NAME = "proxy-whitelist.txt";
+
+ private final FastLogin plugin;
+ //null if whitelist is empty so bungeecord support is disabled
+ private final UUID proxyId;
+
+ public BungeeCordListener(FastLogin plugin) {
+ this.plugin = plugin;
+ this.proxyId = loadBungeeCordId();
+ }
+
+ @Override
+ public void onPluginMessageReceived(String channel, Player player, byte[] message) {
+ if (!channel.equals(plugin.getName())) {
+ return;
+ }
+
+ ByteArrayDataInput dataInput = ByteStreams.newDataInput(message);
+ String subchannel = dataInput.readUTF();
+ plugin.getLogger().log(Level.FINEST, "Received plugin message for subchannel {0} from {1}"
+ , new Object[]{subchannel, player});
+ if ("Checked".equalsIgnoreCase(subchannel)) {
+ //bungeecord UUID
+ long mostSignificantBits = dataInput.readLong();
+ long leastSignificantBits = dataInput.readLong();
+ UUID sourceId = new UUID(mostSignificantBits, leastSignificantBits);
+ //fails too if no proxy id is specified in the whitelist file
+ if (sourceId.equals(proxyId)) {
+ //make sure the proxy is allowed to transfer data to us
+ String playerName = dataInput.readUTF();
+ //check if the player is still online or disconnected
+ Player checkedPlayer = plugin.getServer().getPlayerExact(playerName);
+ if (checkedPlayer != null && checkedPlayer.isOnline()) {
+ PlayerSession playerSession = new PlayerSession(playerName, null, null);
+ playerSession.setVerified(true);
+ //put it only if the user doesn't has a session open
+ //so that the player have to send the bungeecord packet and cannot skip the verification then
+ plugin.getSessions().putIfAbsent(checkedPlayer.getAddress().toString(), playerSession);
+ }
+ }
+ }
+ }
+
+ public UUID loadBungeeCordId() {
+ File whitelistFile = new File(plugin.getDataFolder(), FILE_NAME);
+ //create a new folder if it doesn't exist. Fail silently otherwise
+ whitelistFile.getParentFile().mkdir();
+ try {
+ if (!whitelistFile.exists()) {
+ whitelistFile.createNewFile();
+ }
+
+ String firstLine = Files.readFirstLine(whitelistFile, Charsets.UTF_8);
+ if (firstLine != null && !firstLine.isEmpty()) {
+ return UUID.fromString(firstLine.trim());
+ }
+ } catch (IOException ex) {
+ plugin.getLogger().log(Level.SEVERE, "Failed to create file for Proxy whitelist", ex);
+ } catch (Exception ex) {
+ plugin.getLogger().log(Level.SEVERE, "Failed to retrieve proxy Id. Disabling BungeeCord support", ex);
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/com/github/games647/fastlogin/listener/EncryptionPacketListener.java b/src/main/java/com/github/games647/fastlogin/listener/EncryptionPacketListener.java
index 3e05fac7..bbd95d0d 100644
--- a/src/main/java/com/github/games647/fastlogin/listener/EncryptionPacketListener.java
+++ b/src/main/java/com/github/games647/fastlogin/listener/EncryptionPacketListener.java
@@ -147,11 +147,11 @@ public class EncryptionPacketListener extends PacketAdapter {
//try to get the networkManager from ProtocolLib
private Object getNetworkManager(Player player)
throws IllegalAccessException, NoSuchFieldException {
- Object injector = TemporaryPlayerFactory.getInjectorFromPlayer(player);
- Field injectorField = injector.getClass().getDeclaredField("injector");
+ Object socketInjector = TemporaryPlayerFactory.getInjectorFromPlayer(player);
+ Field injectorField = socketInjector.getClass().getDeclaredField("injector");
injectorField.setAccessible(true);
- Object rawInjector = injectorField.get(injector);
+ Object rawInjector = injectorField.get(socketInjector);
injectorField = rawInjector.getClass().getDeclaredField("networkManager");
injectorField.setAccessible(true);
@@ -236,7 +236,7 @@ public class EncryptionPacketListener extends PacketAdapter {
//fake a new login packet in order to let the server handle all the other stuff
private void receiveFakeStartPacket(String username, Player from) {
//see StartPacketListener for packet information
- PacketContainer startPacket = protocolManager.createPacket(PacketType.Login.Client.START, true);
+ PacketContainer startPacket = protocolManager.createPacket(PacketType.Login.Client.START);
//uuid is ignored by the packet definition
WrappedGameProfile fakeProfile = new WrappedGameProfile(UUID.randomUUID(), username);
diff --git a/src/main/java/com/github/games647/fastlogin/listener/HandshakePacketListener.java b/src/main/java/com/github/games647/fastlogin/listener/HandshakePacketListener.java
new file mode 100644
index 00000000..e143f129
--- /dev/null
+++ b/src/main/java/com/github/games647/fastlogin/listener/HandshakePacketListener.java
@@ -0,0 +1,58 @@
+package com.github.games647.fastlogin.listener;
+
+import com.comphenix.protocol.PacketType;
+import com.comphenix.protocol.events.PacketAdapter;
+import com.comphenix.protocol.events.PacketContainer;
+import com.comphenix.protocol.events.PacketEvent;
+import com.github.games647.fastlogin.FastLogin;
+
+import java.util.logging.Level;
+
+/**
+ * Listens to incoming handshake packets.
+ *
+ * As BungeeCord sends additional information on the Handshake,
+ * we can detect it and check so if the player is coming from a
+ * BungeeCord instance. IpForward has to be activated in the
+ * BungeeCord config to send these extra information.
+ *
+ * Packet information:
+ * http://wiki.vg/Protocol#Handshake
+ *
+ * Int=Protocol version
+ * String=connecting server address (and additional information from BungeeCord)
+ * int=server port
+ * int=next state
+ */
+public class HandshakePacketListener extends PacketAdapter {
+
+ //hides the inherit Plugin plugin field, but we need a more detailed type than just Plugin
+ private final FastLogin plugin;
+
+ public HandshakePacketListener(FastLogin plugin) {
+ //run async in order to not block the server, because we are making api calls to Mojang
+ super(params(plugin, PacketType.Handshake.Client.SET_PROTOCOL).optionAsync());
+
+ this.plugin = plugin;
+ }
+
+ @Override
+ public void onPacketReceiving(PacketEvent packetEvent) {
+ PacketContainer packet = packetEvent.getPacket();
+ PacketType.Protocol nextProtocol = packet.getProtocols().read(0);
+
+ //we don't want to listen for server ping.
+ if (nextProtocol == PacketType.Protocol.LOGIN) {
+ //here are the information written separated by a space
+ String hostname = packet.getStrings().read(0);
+ //https://hub.spigotmc.org/stash/projects/SPIGOT/repos/spigot/browse/CraftBukkit-Patches/0055-BungeeCord-Support.patch
+ String[] split = hostname.split("\00");
+ if (split.length == 3 || split.length == 4) {
+ plugin.getLogger().log(Level.FINER, "Detected BungeeCord for {0}", hostname);
+
+ //object = because there are no concurrent sets with weak keys
+ plugin.getBungeeCordUsers().put(packetEvent.getPlayer(), new Object());
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/github/games647/fastlogin/listener/PlayerListener.java b/src/main/java/com/github/games647/fastlogin/listener/PlayerListener.java
deleted file mode 100644
index 16a97ab3..00000000
--- a/src/main/java/com/github/games647/fastlogin/listener/PlayerListener.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.github.games647.fastlogin.listener;
-
-import com.github.games647.fastlogin.FastLogin;
-import com.github.games647.fastlogin.PlayerSession;
-import com.github.games647.fastlogin.hooks.AuthPlugin;
-
-import java.util.logging.Level;
-
-import org.bukkit.Bukkit;
-import org.bukkit.entity.Player;
-import org.bukkit.event.EventHandler;
-import org.bukkit.event.Listener;
-import org.bukkit.event.player.PlayerJoinEvent;
-
-/**
- * This listener tells authentication plugins if the player
- * has a premium account and we checked it successfully. So the
- * plugin can skip authentication.
- */
-public class PlayerListener implements Listener {
-
- private static final long DELAY_LOGIN = 1 * 20L;
-
- private final FastLogin plugin;
- private final AuthPlugin authPlugin;
-
- public PlayerListener(FastLogin plugin, AuthPlugin authPlugin) {
- this.plugin = plugin;
- this.authPlugin = authPlugin;
- }
-
- @EventHandler(ignoreCancelled = true)
- public void onJoin(PlayerJoinEvent joinEvent) {
- final Player player = joinEvent.getPlayer();
- String address = player.getAddress().toString();
-
- //removing the session because we now use it
- PlayerSession session = plugin.getSessions().remove(address);
- //check if it's the same player as we checked before
- if (session != null && player.getName().equals(session.getUsername()) && session.isVerified()) {
- Bukkit.getScheduler().runTaskLater(plugin, new Runnable() {
-
- @Override
- public void run() {
- if (player.isOnline()) {
- plugin.getLogger().log(Level.FINE, "Logging player {0} in", player.getName());
- authPlugin.forceLogin(player);
- }
- }
- //Wait before auth plugin initializes the player
- }, DELAY_LOGIN);
- }
- }
-}
diff --git a/src/main/java/com/github/games647/fastlogin/listener/StartPacketListener.java b/src/main/java/com/github/games647/fastlogin/listener/StartPacketListener.java
index a7b5009a..65171744 100644
--- a/src/main/java/com/github/games647/fastlogin/listener/StartPacketListener.java
+++ b/src/main/java/com/github/games647/fastlogin/listener/StartPacketListener.java
@@ -68,7 +68,7 @@ public class StartPacketListener extends PacketAdapter {
*/
@Override
public void onPacketReceiving(PacketEvent packetEvent) {
- Player player = packetEvent.getPlayer();
+ final Player player = packetEvent.getPlayer();
//this includes ip:port. Should be unique for an incoming login request with a timeout of 2 minutes
String sessionKey = player.getAddress().toString();
@@ -81,7 +81,8 @@ public class StartPacketListener extends PacketAdapter {
String username = packet.getGameProfiles().read(0).getName();
plugin.getLogger().log(Level.FINER, "Player {0} with {1} connecting to the server"
, new Object[]{sessionKey, username});
- if (plugin.getEnabledPremium().contains(username) && isPremiumName(username)) {
+ if (!plugin.getBungeeCordUsers().containsKey(player)
+ && plugin.getEnabledPremium().contains(username) && isPremiumName(username)) {
//minecraft server implementation
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L161
sentEncryptionRequest(sessionKey, username, player, packetEvent);
@@ -117,7 +118,7 @@ public class StartPacketListener extends PacketAdapter {
* key=public server key
* verifyToken=random 4 byte array
*/
- PacketContainer newPacket = protocolManager.createPacket(PacketType.Login.Server.ENCRYPTION_BEGIN, true);
+ PacketContainer newPacket = protocolManager.createPacket(PacketType.Login.Server.ENCRYPTION_BEGIN);
//randomized server id to make sure the request is for our server
//this could be relevant http://www.sk89q.com/2011/09/minecraft-name-spoofing-exploit/