Add support for multiple bungeecords (Fixes #19)

This commit is contained in:
games647
2016-05-28 17:21:08 +02:00
parent 3e844be65d
commit cb876a52bd
24 changed files with 187 additions and 217 deletions

2
.gitignore vendored
View File

@ -46,4 +46,4 @@ gradle-app.setting
bukkit/target
universal/target
bungee/target
core/target
core/target

View File

@ -1,3 +1,8 @@
######1.4
* Added Bungee setAuthPlugin method
* Multiple BungeeCord support
######1.3.1
* Prevent thread create violation in BungeeCord
@ -127,4 +132,4 @@
* Added better error handling
#####0.1
* First release
* First release

View File

@ -1,15 +1,10 @@
package com.github.games647.fastlogin.bukkit;
import com.comphenix.protocol.utility.SafeCacheBuilder;
import com.github.games647.fastlogin.core.FastLoginCore;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.google.common.cache.CacheLoader;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.io.File;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
public class BukkitCore extends FastLoginCore {
@ -30,21 +25,6 @@ public class BukkitCore extends FastLoginCore {
return plugin.getLogger();
}
@Override
public ConcurrentMap<String, PlayerProfile> buildCache() {
return SafeCacheBuilder
.<String, PlayerProfile>newBuilder()
.concurrencyLevel(20)
.expireAfterAccess(30, TimeUnit.MINUTES)
.build(new CacheLoader<String, PlayerProfile>() {
@Override
public PlayerProfile load(String key) throws Exception {
//should be fetched manually
throw new UnsupportedOperationException("Not supported yet.");
}
});
}
@Override
public ThreadFactory getThreadFactory() {
String pluginName = plugin.getName();

View File

@ -1,6 +1,8 @@
package com.github.games647.fastlogin.bukkit;
import com.comphenix.protocol.wrappers.WrappedSignedProperty;
import com.github.games647.fastlogin.core.LoginSession;
import com.github.games647.fastlogin.core.PlayerProfile;
import java.util.UUID;
@ -11,33 +13,32 @@ import org.apache.commons.lang.ArrayUtils;
*
* This session is invalid if the player disconnects or the login was successful
*/
public class PlayerSession {
public class BukkitLoginSession extends LoginSession {
private final String username;
private final String serverId;
private final byte[] verifyToken;
private UUID uuid;
private WrappedSignedProperty skinProperty;
private boolean verified;
private boolean registered;
public PlayerSession(String username, String serverId, byte[] verifyToken) {
this.username = username;
public BukkitLoginSession(String username, String serverId, byte[] verifyToken, boolean registered
, PlayerProfile profile) {
super(username, registered, profile);
this.serverId = serverId;
this.verifyToken = ArrayUtils.clone(verifyToken);
}
public PlayerSession(String username) {
this(username, "", ArrayUtils.EMPTY_BYTE_ARRAY);
//available for bungeecord
public BukkitLoginSession(String username, boolean registered) {
this(username, "", ArrayUtils.EMPTY_BYTE_ARRAY, registered, null);
}
/**
* Gets the random generated server id. This makes sure the request
* sent from the client is just for this server.
* Gets the random generated server id. This makes sure the request sent from the client is just for this server.
*
* See this for details
* http://www.sk89q.com/2011/09/minecraft-name-spoofing-exploit/
* See this for details http://www.sk89q.com/2011/09/minecraft-name-spoofing-exploit/
*
* Empty if it's a BungeeCord connection
*
@ -58,15 +59,6 @@ public class PlayerSession {
return ArrayUtils.clone(verifyToken);
}
/**
* Gets the username the player sent to the server
*
* @return the client sent username
*/
public String getUsername() {
return username;
}
/**
* Gets the premium skin of this player
*
@ -86,26 +78,7 @@ public class PlayerSession {
}
/**
* Sets whether the account of this player already exists
*
* @param registered whether the account exists
*/
public synchronized void setRegistered(boolean registered) {
this.registered = registered;
}
/**
* Gets whether the account of this player already exists.
*
* @return whether the account exists
*/
public synchronized boolean needsRegistration() {
return !registered;
}
/**
* Sets whether the player has a premium (paid account) account
* and valid session
* Sets whether the player has a premium (paid account) account and valid session
*
* @param verified whether the player has valid session
*/
@ -122,7 +95,6 @@ public class PlayerSession {
return uuid;
}
/**
* Set the online UUID if it's fetched
*
@ -133,8 +105,7 @@ public class PlayerSession {
}
/**
* Get whether the player has a premium (paid account) account
* and valid session
* Get whether the player has a premium (paid account) account and valid session
*
* @return whether the player has a valid session
*/

View File

@ -40,14 +40,14 @@ public class FastLoginBukkit extends JavaPlugin {
//this map is thread-safe for async access (Packet Listener)
//SafeCacheBuilder is used in order to be version independent
private final ConcurrentMap<String, PlayerSession> session = SafeCacheBuilder.<String, PlayerSession>newBuilder()
private final ConcurrentMap<String, BukkitLoginSession> session = SafeCacheBuilder.<String, BukkitLoginSession>newBuilder()
//2 minutes should be enough as a timeout for bad internet connection (Server, Client and Mojang)
.expireAfterWrite(1, TimeUnit.MINUTES)
//mapped by ip:port -> PlayerSession
.build(new CacheLoader<String, PlayerSession>() {
.build(new CacheLoader<String, BukkitLoginSession>() {
@Override
public PlayerSession load(String key) throws Exception {
public BukkitLoginSession load(String key) throws Exception {
//A key should be inserted manually on start packet
throw new UnsupportedOperationException("Not supported");
}
@ -158,7 +158,7 @@ public class FastLoginBukkit extends JavaPlugin {
*
* @return a thread-safe session map
*/
public ConcurrentMap<String, PlayerSession> getSessions() {
public ConcurrentMap<String, BukkitLoginSession> getSessions() {
return session;
}

View File

@ -17,7 +17,7 @@ import org.bukkit.entity.Player;
public class ForceLoginTask implements Runnable {
protected final FastLoginBukkit plugin;
private final FastLoginBukkit plugin;
protected final Player player;
public ForceLoginTask(FastLoginBukkit plugin, Player player) {
@ -33,14 +33,12 @@ public class ForceLoginTask implements Runnable {
//remove the bungeecord identifier if there is ones
String id = '/' + player.getAddress().getAddress().getHostAddress() + ':' + player.getAddress().getPort();
PlayerSession session = plugin.getSessions().get(id);
BukkitAuthPlugin authPlugin = plugin.getAuthPlugin();
BukkitLoginSession session = plugin.getSessions().remove(id);
Storage storage = plugin.getCore().getStorage();
PlayerProfile playerProfile = null;
if (storage != null) {
playerProfile = storage.getProfile(player.getName(), false);
if (session != null) {
playerProfile = session.getProfile();
}
if (session == null) {
@ -53,6 +51,7 @@ public class ForceLoginTask implements Runnable {
//check if it's the same player as we checked before
} else if (player.getName().equals(session.getUsername())) {
//premium player
BukkitAuthPlugin authPlugin = plugin.getAuthPlugin();
if (authPlugin == null) {
//maybe only bungeecord plugin
sendSuccessNotification();

View File

@ -25,11 +25,11 @@ public class MojangApiBukkit extends MojangApiConnector {
@Override
public boolean hasJoinedServer(Object session, String serverId) {
if (!(session instanceof PlayerSession)) {
if (!(session instanceof BukkitLoginSession)) {
return false;
}
PlayerSession playerSession = (PlayerSession) session;
BukkitLoginSession playerSession = (BukkitLoginSession) session;
try {
String url = HAS_JOINED_URL + "username=" + playerSession.getUsername() + "&serverId=" + serverId;
HttpURLConnection conn = getConnection(url);

View File

@ -34,7 +34,7 @@ public class CrackedCommand implements CommandExecutor {
sender.sendMessage(ChatColor.YELLOW + "Sending request...");
} else {
//todo: load async if it's not in the cache anymore
final PlayerProfile profile = plugin.getCore().getStorage().getProfile(sender.getName(), true);
final PlayerProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName());
if (profile.isPremium()) {
sender.sendMessage(ChatColor.DARK_GREEN + "Removed from the list of premium players");
profile.setPremium(false);
@ -62,7 +62,7 @@ public class CrackedCommand implements CommandExecutor {
sender.sendMessage(ChatColor.YELLOW + "Sending request for player " + args[0] + "...");
} else {
//todo: load async if it's not in the cache anymore
final PlayerProfile profile = plugin.getCore().getStorage().getProfile(args[0], true);
final PlayerProfile profile = plugin.getCore().getStorage().loadProfile(args[0]);
if (profile == null) {
sender.sendMessage(ChatColor.DARK_RED + "Player not in the database");
return true;

View File

@ -39,7 +39,7 @@ public class PremiumCommand implements CommandExecutor {
sender.sendMessage(ChatColor.YELLOW + "Sending request...");
} else {
// //todo: load async if it's not in the cache anymore
final PlayerProfile profile = plugin.getCore().getStorage().getProfile(sender.getName(), true);
final PlayerProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName());
if (profile.isPremium()) {
sender.sendMessage(ChatColor.DARK_RED + "You are already on the premium list");
} else {
@ -68,7 +68,7 @@ public class PremiumCommand implements CommandExecutor {
sender.sendMessage(ChatColor.YELLOW + "Sending request...");
} else {
//todo: load async if it's not in the cache anymore
final PlayerProfile profile = plugin.getCore().getStorage().getProfile(args[0], true);
final PlayerProfile profile = plugin.getCore().getStorage().loadProfile(args[0]);
if (profile == null) {
sender.sendMessage(ChatColor.DARK_RED + "Player not in the database");
return true;

View File

@ -4,7 +4,7 @@ import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedSignedProperty;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.ForceLoginTask;
import com.github.games647.fastlogin.bukkit.PlayerSession;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@ -14,7 +14,6 @@ import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerLoginEvent.Result;
import org.bukkit.event.player.PlayerQuitEvent;
/**
* This listener tells authentication plugins if the player has a premium account and we checked it successfully. So the
@ -41,7 +40,7 @@ public class BukkitJoinListener implements Listener {
public void onPlayerJoin(PlayerJoinEvent joinEvent) {
Player player = joinEvent.getPlayer();
PlayerSession session = plugin.getSessions().get(player.getAddress().toString());
BukkitLoginSession session = plugin.getSessions().get(player.getAddress().toString());
if (session != null && plugin.getConfig().getBoolean("forwardSkin")) {
WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(player);
WrappedSignedProperty skin = session.getSkin();
@ -55,12 +54,4 @@ public class BukkitJoinListener implements Listener {
Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, new ForceLoginTask(plugin, player), DELAY_LOGIN);
}
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent quitEvent) {
Player player = quitEvent.getPlayer();
//prevent memory leaks
player.removeMetadata(plugin.getName(), plugin);
}
}

View File

@ -2,15 +2,18 @@ package com.github.games647.fastlogin.bukkit.listener;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.ForceLoginTask;
import com.github.games647.fastlogin.bukkit.PlayerSession;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
import com.google.common.base.Charsets;
import com.google.common.collect.Sets;
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.List;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
@ -31,11 +34,11 @@ public class BungeeCordListener implements PluginMessageListener {
protected final FastLoginBukkit plugin;
//null if whitelist is empty so bungeecord support is disabled
private final UUID proxyId;
private final Set<UUID> proxyIds;
public BungeeCordListener(FastLoginBukkit plugin) {
this.plugin = plugin;
this.proxyId = loadBungeeCordId();
this.proxyIds = loadBungeeCordIds();
}
@Override
@ -65,17 +68,14 @@ public class BungeeCordListener implements PluginMessageListener {
plugin.getLogger().log(Level.FINEST, "Received proxy id {0} from {1}", new Object[]{sourceId, player});
//fail if BungeeCord support is disabled (id = null)
if (sourceId.equals(proxyId)) {
final PlayerSession playerSession = new PlayerSession(playerName);
if (proxyIds.contains(sourceId)) {
final String id = '/' + checkedPlayer.getAddress().getAddress().getHostAddress() + ':'
+ checkedPlayer.getAddress().getPort();
if ("AUTO_LOGIN".equalsIgnoreCase(subchannel)) {
BukkitLoginSession playerSession = new BukkitLoginSession(playerName, true);
playerSession.setVerified(true);
playerSession.setRegistered(true);
plugin.getSessions().put(id, playerSession);
} else if ("AUTO_REGISTER".equalsIgnoreCase(subchannel)) {
playerSession.setVerified(true);
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
@Override
public void run() {
@ -83,6 +83,8 @@ public class BungeeCordListener implements PluginMessageListener {
try {
//we need to check if the player is registered on Bukkit too
if (authPlugin != null && !authPlugin.isRegistered(playerName)) {
BukkitLoginSession playerSession = new BukkitLoginSession(playerName, false);
playerSession.setVerified(true);
plugin.getSessions().put(id, playerSession);
}
} catch (Exception ex) {
@ -97,7 +99,7 @@ public class BungeeCordListener implements PluginMessageListener {
}
}
public UUID loadBungeeCordId() {
public Set<UUID> loadBungeeCordIds() {
File whitelistFile = new File(plugin.getDataFolder(), FILE_NAME);
//create a new folder if it doesn't exist. Fail silently otherwise
whitelistFile.getParentFile().mkdir();
@ -106,9 +108,16 @@ public class BungeeCordListener implements PluginMessageListener {
whitelistFile.createNewFile();
}
String firstLine = Files.readFirstLine(whitelistFile, Charsets.UTF_8);
if (firstLine != null && !firstLine.isEmpty()) {
return UUID.fromString(firstLine.trim());
Set<UUID> ids = Sets.newHashSet();
List<String> lines = Files.readLines(whitelistFile, Charsets.UTF_8);
for (String line : lines) {
if (line == null || line.trim().isEmpty()) {
continue;
}
UUID uuid = UUID.fromString(line.trim());
ids.add(uuid);
}
} catch (IOException ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to create file for Proxy whitelist", ex);

View File

@ -1,7 +1,7 @@
package com.github.games647.fastlogin.bukkit.listener;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.PlayerSession;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
import com.github.games647.fastlogin.core.PlayerProfile;
@ -33,18 +33,18 @@ public class ProtocolSupportListener implements Listener {
String username = loginStartEvent.getName();
//remove old data every time on a new login in order to keep the session only for one person
plugin.getSessions().remove(username);
plugin.getSessions().remove(loginStartEvent.getAddress().toString());
BukkitAuthPlugin authPlugin = plugin.getAuthPlugin();
if (authPlugin == null) {
return;
}
PlayerProfile playerProfile = plugin.getCore().getStorage().getProfile(username, true);
PlayerProfile playerProfile = plugin.getCore().getStorage().loadProfile(username);
if (playerProfile != null) {
if (playerProfile.isPremium()) {
if (playerProfile.getUserId() != -1) {
startPremiumSession(username, loginStartEvent, true);
startPremiumSession(username, loginStartEvent, true, playerProfile);
}
} else if (playerProfile.getUserId() == -1) {
//user not exists in the db
@ -53,7 +53,7 @@ public class ProtocolSupportListener implements Listener {
UUID premiumUUID = plugin.getCore().getMojangApiConnector().getPremiumUUID(username);
if (premiumUUID != null) {
plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
startPremiumSession(username, loginStartEvent, false);
startPremiumSession(username, loginStartEvent, false, playerProfile);
}
}
} catch (Exception ex) {
@ -68,19 +68,19 @@ public class ProtocolSupportListener implements Listener {
//skin was resolved -> premium player
if (propertiesResolveEvent.hasProperty("textures")) {
InetSocketAddress address = propertiesResolveEvent.getAddress();
PlayerSession session = plugin.getSessions().get(address.toString());
BukkitLoginSession session = plugin.getSessions().get(address.toString());
if (session != null) {
session.setVerified(true);
}
}
}
private void startPremiumSession(String playerName, PlayerLoginStartEvent loginStartEvent, boolean registered) {
private void startPremiumSession(String playerName, PlayerLoginStartEvent loginStartEvent, boolean registered
, PlayerProfile playerProfile) {
loginStartEvent.setOnlineMode(true);
InetSocketAddress address = loginStartEvent.getAddress();
PlayerSession playerSession = new PlayerSession(playerName, null, null);
playerSession.setRegistered(registered);
BukkitLoginSession playerSession = new BukkitLoginSession(playerName, null, null, registered, playerProfile);
plugin.getSessions().put(address.toString(), playerSession);
if (plugin.getConfig().getBoolean("premiumUuid")) {
loginStartEvent.setUseOnlineModeUUID(true);

View File

@ -11,7 +11,7 @@ import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.github.games647.fastlogin.bukkit.EncryptionUtil;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.PlayerSession;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
@ -73,13 +73,10 @@ public class EncryptionPacketListener extends PacketAdapter {
public void onPacketReceiving(PacketEvent packetEvent) {
Player player = packetEvent.getPlayer();
//the player name is unknown to ProtocolLib (so getName() doesn't work) - now uses ip:port as key
String uniqueSessionKey = player.getAddress().toString();
PlayerSession session = plugin.getSessions().get(uniqueSessionKey);
BukkitLoginSession session = plugin.getSessions().get(player.getAddress().toString());
if (session == null) {
disconnect(packetEvent, "Invalid request", Level.FINE
, "Player {0} tried to send encryption response at invalid state"
, player.getAddress());
, "Player {0} tried to send encryption response at invalid state", player.getAddress());
return;
}
@ -118,7 +115,7 @@ public class EncryptionPacketListener extends PacketAdapter {
packetEvent.setCancelled(true);
}
private void setPremiumUUID(PlayerSession session, Player player) {
private void setPremiumUUID(BukkitLoginSession session, Player player) {
UUID uuid = session.getUuid();
if (plugin.getConfig().getBoolean("premiumUuid") && uuid != null) {
try {
@ -132,7 +129,7 @@ public class EncryptionPacketListener extends PacketAdapter {
}
}
private boolean checkVerifyToken(PlayerSession session, PrivateKey privateKey, PacketEvent packetEvent) {
private boolean checkVerifyToken(BukkitLoginSession session, PrivateKey privateKey, PacketEvent packetEvent) {
byte[] requestVerify = session.getVerifyToken();
//encrypted verify token
byte[] responseVerify = packetEvent.getPacket().getByteArrays().read(1);

View File

@ -6,9 +6,9 @@ import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.github.games647.fastlogin.bukkit.PlayerSession;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
import com.github.games647.fastlogin.core.PlayerProfile;
import java.lang.reflect.InvocationTargetException;
import java.security.PublicKey;
@ -83,20 +83,20 @@ public class StartPacketListener extends PacketAdapter {
return;
}
PlayerProfile playerProfile = plugin.getCore().getStorage().getProfile(username, true);
if (playerProfile != null) {
if (playerProfile.isPremium()) {
if (playerProfile.getUserId() != -1) {
enablePremiumLogin(username, sessionKey, player, packetEvent, true);
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(username);
if (profile != null) {
if (profile.isPremium()) {
if (profile.getUserId() != -1) {
enablePremiumLogin(username, profile, sessionKey, player, packetEvent, true);
}
} else if (playerProfile.getUserId() == -1) {
} else if (profile.getUserId() == -1) {
//user not exists in the db
try {
if (plugin.getConfig().getBoolean("autoRegister") && !authPlugin.isRegistered(username)) {
UUID premiumUUID = plugin.getCore().getMojangApiConnector().getPremiumUUID(username);
if (premiumUUID != null) {
plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
enablePremiumLogin(username, sessionKey, player, packetEvent, false);
enablePremiumLogin(username, profile, sessionKey, player, packetEvent, false);
}
}
} catch (Exception ex) {
@ -108,8 +108,8 @@ public class StartPacketListener extends PacketAdapter {
//minecraft server implementation
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L161
private void enablePremiumLogin(String username, String sessionKey, Player player, PacketEvent packetEvent
, boolean registered) {
private void enablePremiumLogin(String username, PlayerProfile profile, String sessionKey, Player player
, PacketEvent packetEvent, boolean registered) {
//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/
String serverId = Long.toString(random.nextLong(), 16);
@ -120,8 +120,8 @@ public class StartPacketListener extends PacketAdapter {
boolean success = sentEncryptionRequest(player, serverId, verifyToken);
if (success) {
PlayerSession playerSession = new PlayerSession(username, serverId, verifyToken);
playerSession.setRegistered(registered);
BukkitLoginSession playerSession = new BukkitLoginSession(username, serverId
, verifyToken, registered, profile);
plugin.getSessions().put(sessionKey, playerSession);
//cancel only if the player has a paid account otherwise login as normal offline player
packetEvent.setCancelled(true);

View File

@ -2,9 +2,12 @@ package com.github.games647.fastlogin.bungee;
import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.bungee.hooks.BungeeAuthPlugin;
import com.github.games647.fastlogin.core.LoginSession;
import com.github.games647.fastlogin.core.PlayerProfile;
import java.util.UUID;
import java.util.logging.Level;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.event.PreLoginEvent;
@ -21,15 +24,18 @@ public class AsyncPremiumCheck implements Runnable {
@Override
public void run() {
PendingConnection connection = preLoginEvent.getConnection();
plugin.getSession().remove(connection);
String username = connection.getName();
try {
PlayerProfile playerProfile = plugin.getCore().getStorage().getProfile(username, true);
if (playerProfile != null) {
if (playerProfile.isPremium()) {
if (playerProfile.getUserId() != -1) {
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(username);
if (profile != null) {
if (profile.isPremium()) {
if (profile.getUserId() != -1) {
plugin.getSession().put(connection, new LoginSession(username, true, profile));
connection.setOnlineMode(true);
}
} else if (playerProfile.getUserId() == -1) {
} else if (profile.getUserId() == -1) {
//user not exists in the db
BungeeAuthPlugin authPlugin = plugin.getBungeeAuthPlugin();
if (plugin.getConfiguration().getBoolean("autoRegister")
@ -38,7 +44,7 @@ public class AsyncPremiumCheck implements Runnable {
if (premiumUUID != null) {
plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
connection.setOnlineMode(true);
plugin.getPendingAutoRegister().put(connection, new Object());
plugin.getSession().put(connection, new LoginSession(username, false, profile));
}
}
}

View File

@ -1,14 +1,10 @@
package com.github.games647.fastlogin.bungee;
import com.github.games647.fastlogin.core.FastLoginCore;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.google.common.cache.CacheBuilder;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.io.File;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import net.md_5.bungee.api.scheduler.GroupedThreadFactory;
@ -31,15 +27,6 @@ public class BungeeCore extends FastLoginCore {
return plugin.getLogger();
}
@Override
public ConcurrentMap<String, PlayerProfile> buildCache() {
return CacheBuilder
.newBuilder()
.concurrencyLevel(20)
.expireAfterAccess(30, TimeUnit.MINUTES)
.<String, PlayerProfile>build().asMap();
}
@Override
public ThreadFactory getThreadFactory() {
String pluginName = plugin.getDescription().getName();

View File

@ -5,6 +5,7 @@ import com.github.games647.fastlogin.bungee.hooks.BungeeAuthPlugin;
import com.github.games647.fastlogin.bungee.listener.PlayerConnectionListener;
import com.github.games647.fastlogin.bungee.listener.PluginMessageListener;
import com.github.games647.fastlogin.core.FastLoginCore;
import com.github.games647.fastlogin.core.LoginSession;
import com.google.common.cache.CacheBuilder;
import java.io.File;
@ -41,6 +42,11 @@ public class FastLoginBungee extends Plugin {
.expireAfterWrite(1, TimeUnit.MINUTES)
.<PendingConnection, Object>build().asMap();
private final ConcurrentMap<PendingConnection, LoginSession> session = CacheBuilder
.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES)
.<PendingConnection, LoginSession>build().asMap();
@Override
public void onEnable() {
loginCore.setMojangApiConnector(new MojangApiBungee(loginCore));
@ -112,8 +118,8 @@ public class FastLoginBungee extends Plugin {
return configuration;
}
public ConcurrentMap<PendingConnection, Object> getPendingAutoRegister() {
return pendingAutoRegister;
public ConcurrentMap<PendingConnection, LoginSession> getSession() {
return session;
}
/**

View File

@ -1,11 +1,13 @@
package com.github.games647.fastlogin.bungee;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.github.games647.fastlogin.bungee.hooks.BungeeAuthPlugin;
import com.github.games647.fastlogin.core.LoginSession;
import com.github.games647.fastlogin.core.PlayerProfile;
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;
@ -24,17 +26,19 @@ public class ForceLoginTask implements Runnable {
@Override
public void run() {
PlayerProfile playerProfile = plugin.getCore().getStorage().getProfile(player.getName(), false);
PendingConnection pendingConnection = player.getPendingConnection();
LoginSession session = plugin.getSession().remove(pendingConnection);
PlayerProfile playerProfile = session.getProfile();
//force login only on success
if (player.getPendingConnection().isOnlineMode()) {
boolean autoRegister = plugin.getPendingAutoRegister().remove(player.getPendingConnection()) != null;
if (pendingConnection.isOnlineMode()) {
boolean autoRegister = session.needsRegistration();
BungeeAuthPlugin authPlugin = plugin.getBungeeAuthPlugin();
if (authPlugin == null) {
sendBukkitLoginNotification(autoRegister);
} else if (player.isConnected()) {
if (autoRegister) {
if (session.needsRegistration()) {
String password = plugin.generateStringPassword();
if (authPlugin.forceRegister(player, password)) {
sendBukkitLoginNotification(autoRegister);

View File

@ -14,7 +14,8 @@ public class AsyncStatusMessage implements Runnable {
private final String targetPlayer;
private final boolean toPremium;
public AsyncStatusMessage(FastLoginBungee plugin, ProxiedPlayer fromPlayer, String targetPlayer, boolean toPremium) {
public AsyncStatusMessage(FastLoginBungee plugin, ProxiedPlayer fromPlayer, String targetPlayer
, boolean toPremium) {
this.plugin = plugin;
this.fromPlayer = fromPlayer;
this.targetPlayer = targetPlayer;
@ -31,7 +32,7 @@ public class AsyncStatusMessage implements Runnable {
}
private void turnOffPremium() {
PlayerProfile playerProfile = plugin.getCore().getStorage().getProfile(targetPlayer, true);
PlayerProfile playerProfile = plugin.getCore().getStorage().loadProfile(targetPlayer);
if (!playerProfile.isPremium()) {
if (fromPlayer.isConnected()) {
TextComponent textComponent = new TextComponent("You are not in the premium list");
@ -51,7 +52,7 @@ public class AsyncStatusMessage implements Runnable {
}
private void activatePremium() {
PlayerProfile playerProfile = plugin.getCore().getStorage().getProfile(targetPlayer, true);
PlayerProfile playerProfile = plugin.getCore().getStorage().loadProfile(targetPlayer);
if (playerProfile.isPremium()) {
if (fromPlayer.isConnected()) {
TextComponent textComponent = new TextComponent("You are already on the premium list");
@ -63,7 +64,6 @@ public class AsyncStatusMessage implements Runnable {
}
playerProfile.setPremium(true);
//todo: set uuid
plugin.getCore().getStorage().save(playerProfile);
TextComponent textComponent = new TextComponent("Added to the list of premium players");
textComponent.setColor(ChatColor.DARK_GREEN);

View File

@ -3,6 +3,7 @@ package com.github.games647.fastlogin.bungee.listener;
import com.github.games647.fastlogin.bungee.AsyncPremiumCheck;
import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.bungee.ForceLoginTask;
import com.github.games647.fastlogin.core.LoginSession;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.google.common.base.Charsets;
@ -51,7 +52,8 @@ public class PlayerConnectionListener implements Listener {
PendingConnection connection = player.getPendingConnection();
String username = connection.getName();
if (connection.isOnlineMode()) {
PlayerProfile playerProfile = plugin.getCore().getStorage().getProfile(player.getName(), false);
LoginSession session = plugin.getSession().get(connection);
PlayerProfile playerProfile = session.getProfile();
playerProfile.setUuid(player.getUniqueId());
//bungeecord will do this automatically so override it on disabled option

View File

@ -57,7 +57,7 @@ public class PluginMessageListener implements Listener {
if (fromPlayer.getPendingConnection().isOnlineMode()) {
//bukkit module successfully received and force logged in the user
//update only on success to prevent corrupt data
PlayerProfile playerProfile = plugin.getCore().getStorage().getProfile(fromPlayer.getName(), false);
PlayerProfile playerProfile = plugin.getCore().getStorage().loadProfile(fromPlayer.getName());
playerProfile.setPremium(true);
//we override this in the loginevent
plugin.getCore().getStorage().save(playerProfile);

View File

@ -2,7 +2,6 @@ package com.github.games647.fastlogin.core;
import java.io.File;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -36,8 +35,6 @@ public abstract class FastLoginCore {
public abstract Logger getLogger();
public abstract ConcurrentMap<String, PlayerProfile> buildCache();
public abstract ThreadFactory getThreadFactory();
public boolean setupDatabase(String driver, String host, int port, String database, String user, String password) {

View File

@ -0,0 +1,26 @@
package com.github.games647.fastlogin.core;
public class LoginSession {
private final String username;
private final boolean registered;
private final PlayerProfile profile;
public LoginSession(String username, boolean registered, PlayerProfile profile) {
this.username = username;
this.registered = registered;
this.profile = profile;
}
public String getUsername() {
return username;
}
public boolean needsRegistration() {
return !registered;
}
public PlayerProfile getProfile() {
return profile;
}
}

View File

@ -9,7 +9,6 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
public class Storage {
@ -17,13 +16,11 @@ public class Storage {
private static final String PREMIUM_TABLE = "premium";
private final FastLoginCore core;
private final ConcurrentMap<String, PlayerProfile> profileCache;
private final HikariDataSource dataSource;
public Storage(FastLoginCore core, String driver, String host, int port, String databasePath
, String user, String pass) {
this.core = core;
this.profileCache = core.buildCache();
HikariConfig databaseConfig = new HikariConfig();
databaseConfig.setUsername(user);
@ -33,6 +30,8 @@ public class Storage {
databasePath = databasePath.replace("{pluginDir}", core.getDataFolder().getAbsolutePath());
databaseConfig.setThreadFactory(core.getThreadFactory());
String jdbcUrl = "jdbc:";
if (driver.contains("sqlite")) {
jdbcUrl += "sqlite" + "://" + databasePath;
@ -51,15 +50,15 @@ public class Storage {
con = dataSource.getConnection();
Statement statement = con.createStatement();
String createDataStmt = "CREATE TABLE IF NOT EXISTS " + PREMIUM_TABLE + " ("
+ "`UserID` INTEGER PRIMARY KEY AUTO_INCREMENT, "
+ "`UUID` CHAR(36), "
+ "`Name` VARCHAR(16) NOT NULL, "
+ "`Premium` BOOLEAN NOT NULL, "
+ "`LastIp` VARCHAR(255) NOT NULL, "
+ "`LastLogin` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
+ "UNIQUE (`UUID`), "
+ "UserID INTEGER PRIMARY KEY AUTO_INCREMENT, "
+ "UUID CHAR(36), "
+ "Name VARCHAR(16) NOT NULL, "
+ "Premium BOOLEAN NOT NULL, "
+ "LastIp VARCHAR(255) NOT NULL, "
+ "LastLogin TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
+ "UNIQUE (UUID), "
//the premium shouldn't steal the cracked account by changing the name
+ "UNIQUE (`Name`) "
+ "UNIQUE (Name) "
+ ")";
if (dataSource.getJdbcUrl().contains("sqlite")) {
@ -72,46 +71,39 @@ public class Storage {
}
}
public PlayerProfile getProfile(String name, boolean fetch) {
if (profileCache.containsKey(name)) {
return profileCache.get(name);
} else if (fetch) {
Connection con = null;
try {
con = dataSource.getConnection();
PreparedStatement loadStatement = con.prepareStatement("SELECT * FROM " + PREMIUM_TABLE
+ " WHERE `Name`=? LIMIT 1");
loadStatement.setString(1, name);
public PlayerProfile loadProfile(String name) {
Connection con = null;
try {
con = dataSource.getConnection();
PreparedStatement loadStatement = con.prepareStatement("SELECT * FROM " + PREMIUM_TABLE
+ " WHERE Name=? LIMIT 1");
loadStatement.setString(1, name);
ResultSet resultSet = loadStatement.executeQuery();
if (resultSet.next()) {
long userId = resultSet.getInt(1);
ResultSet resultSet = loadStatement.executeQuery();
if (resultSet.next()) {
long userId = resultSet.getInt(1);
String unparsedUUID = resultSet.getString(2);
UUID uuid;
if (unparsedUUID == null) {
uuid = null;
} else {
uuid = FastLoginCore.parseId(unparsedUUID);
}
// String name = resultSet.getString(3);
boolean premium = resultSet.getBoolean(4);
String lastIp = resultSet.getString(5);
long lastLogin = resultSet.getTimestamp(6).getTime();
PlayerProfile playerProfile = new PlayerProfile(userId, uuid, name, premium, lastIp, lastLogin);
profileCache.put(name, playerProfile);
return playerProfile;
String unparsedUUID = resultSet.getString(2);
UUID uuid;
if (unparsedUUID == null) {
uuid = null;
} else {
PlayerProfile crackedProfile = new PlayerProfile(null, name, false, "");
profileCache.put(name, crackedProfile);
return crackedProfile;
uuid = FastLoginCore.parseId(unparsedUUID);
}
} catch (SQLException sqlEx) {
core.getLogger().log(Level.SEVERE, "Failed to query profile", sqlEx);
} finally {
closeQuietly(con);
boolean premium = resultSet.getBoolean(4);
String lastIp = resultSet.getString(5);
long lastLogin = resultSet.getTimestamp(6).getTime();
PlayerProfile playerProfile = new PlayerProfile(userId, uuid, name, premium, lastIp, lastLogin);
return playerProfile;
} else {
PlayerProfile crackedProfile = new PlayerProfile(null, name, false, "");
return crackedProfile;
}
} catch (SQLException sqlEx) {
core.getLogger().log(Level.SEVERE, "Failed to query profile", sqlEx);
} finally {
closeQuietly(con);
}
return null;
@ -156,7 +148,6 @@ public class Storage {
saveStatement.setString(2, playerProfile.getPlayerName());
saveStatement.setBoolean(3, playerProfile.isPremium());
saveStatement.setString(4, playerProfile.getLastIp());
// saveStatement.setTimestamp(5, new Timestamp(playerProfile.getLastLogin()));
saveStatement.setLong(5, playerProfile.getUserId());
saveStatement.execute();
@ -174,7 +165,6 @@ public class Storage {
public void close() {
dataSource.close();
profileCache.clear();
}
private void closeQuietly(Connection con) {