Share the same force login mangement for less duplicate code

This commit is contained in:
games647
2016-09-22 10:56:31 +02:00
parent 91f41c55de
commit bebb04bdea
23 changed files with 475 additions and 444 deletions
@@ -1,6 +1,7 @@
package com.github.games647.fastlogin.core;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
@@ -12,19 +13,20 @@ import java.sql.Statement;
import java.util.Calendar;
import java.util.Locale;
import java.util.UUID;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Level;
public class AuthStorage {
private static final String PREMIUM_TABLE = "premium";
private final FastLoginCore<?> core;
private final FastLoginCore<?, ?, ?> core;
private final HikariDataSource dataSource;
//a try to fix https://www.spigotmc.org/threads/fastlogin.101192/page-26#post-1874647
private final Calendar calendar = Calendar.getInstance(Locale.US);
public AuthStorage(FastLoginCore<?> core, String driver, String host, int port, String databasePath
public AuthStorage(FastLoginCore<?, ?, ?> core, String driver, String host, int port, String databasePath
, String user, String pass) {
this.core = core;
@@ -32,9 +34,20 @@ public class AuthStorage {
databaseConfig.setUsername(user);
databaseConfig.setPassword(pass);
databaseConfig.setDriverClassName(driver);
databaseConfig.setThreadFactory(core.getThreadFactory());
databasePath = databasePath.replace("{pluginDir}", core.getDataFolder().getAbsolutePath());
ThreadFactoryBuilder threadFactoryBuilder = new ThreadFactoryBuilder()
.setNameFormat(core.getPlugin().getName() + " Database Pool Thread #%1$d")
//Hikari create daemons by default
.setDaemon(true);
ThreadFactory platformThreadFactory = core.getPlugin().getThreadFactory();
if (platformThreadFactory != null) {
threadFactoryBuilder.setThreadFactory(platformThreadFactory);
}
databaseConfig.setThreadFactory(threadFactoryBuilder.build());
databasePath = databasePath.replace("{pluginDir}", core.getPlugin().getDataFolder().getAbsolutePath());
String jdbcUrl = "jdbc:";
if (driver.contains("sqlite")) {
@@ -134,7 +147,7 @@ public class AuthStorage {
return crackedProfile;
}
} catch (SQLException sqlEx) {
core.getLogger().log(Level.SEVERE, "Failed to query profile", sqlEx);
core.getPlugin().getLogger().log(Level.SEVERE, "Failed to query profile", sqlEx);
} finally {
closeQuietly(con);
closeQuietly(loadStmt);
@@ -165,7 +178,7 @@ public class AuthStorage {
return playerProfile;
}
} catch (SQLException sqlEx) {
core.getLogger().log(Level.SEVERE, "Failed to query profile", sqlEx);
core.getPlugin().getLogger().log(Level.SEVERE, "Failed to query profile", sqlEx);
} finally {
closeQuietly(con);
closeQuietly(loadStmt);
@@ -226,7 +239,7 @@ public class AuthStorage {
return true;
} catch (SQLException ex) {
core.getLogger().log(Level.SEVERE, "Failed to save playerProfile", ex);
core.getPlugin().getLogger().log(Level.SEVERE, "Failed to save playerProfile", ex);
} finally {
closeQuietly(con);
closeQuietly(updateStmt);
@@ -246,7 +259,7 @@ public class AuthStorage {
try {
closeable.close();
} catch (Exception closeEx) {
core.getLogger().log(Level.SEVERE, "Failed to close connection", closeEx);
core.getPlugin().getLogger().log(Level.SEVERE, "Failed to close connection", closeEx);
}
}
}
@@ -12,25 +12,31 @@ import com.github.games647.fastlogin.core.importer.Importer;
import com.google.common.cache.CacheLoader;
import com.google.common.collect.Sets;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @param <P> Player class
* @param <C> CommandSender
* @param <T> Plugin class
*/
public abstract class FastLoginCore<P> {
public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
public static <K, V> ConcurrentMap<K, V> buildCache(int expireAfterWrite, int maxSize) {
CompatibleCacheBuilder<Object, Object> builder = CompatibleCacheBuilder.newBuilder();
@@ -64,21 +70,62 @@ public abstract class FastLoginCore<P> {
private final ConcurrentMap<String, Object> pendingLogins = FastLoginCore.buildCache(5, 0);
private final Set<UUID> pendingConfirms = Sets.newHashSet();
private final SharedConfig sharedConfig;
private final T plugin;
private SharedConfig sharedConfig;
private MojangApiConnector apiConnector;
private AuthStorage storage;
private PasswordGenerator<P> passwordGenerator = new DefaultPasswordGenerator<>();
private AuthPlugin<P> authPlugin;
public FastLoginCore(Map<String, Object> config) {
this.sharedConfig = new SharedConfig(config);
public FastLoginCore(T plugin) {
this.plugin = plugin;
}
public void setApiConnector() {
public void load() {
saveDefaultFile("messages.yml");
saveDefaultFile("config.yml");
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("config.yml")));
sharedConfig = new SharedConfig(plugin.loadYamlFile(reader));
reader.close();
reader = Files.newBufferedReader(new File(plugin.getDataFolder(), "config.yml").toPath());
sharedConfig.getConfigValues().putAll(plugin.loadYamlFile(reader));
reader.close();
reader = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("messages.yml")));
reader = Files.newBufferedReader(new File(plugin.getDataFolder(), "messages.yml").toPath());
Map<String, Object> messageConfig = plugin.loadYamlFile(reader);
reader.close();
reader = Files.newBufferedReader(new File(plugin.getDataFolder(), "messages.yml").toPath());
messageConfig.putAll(plugin.loadYamlFile(reader));
for (Entry<String, Object> entry : messageConfig.entrySet()) {
String message = plugin.translateColorCodes('&', (String) entry.getValue());
if (!message.isEmpty()) {
localeMessages.put(entry.getKey(), message);
}
}
reader.close();
} catch (IOException ioEx) {
plugin.getLogger().log(Level.INFO, "Failed to load yaml files", ioEx);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException ex) {
plugin.getLogger().log(Level.SEVERE, null, ex);
}
}
}
List<String> ipAddresses = sharedConfig.get("ip-addresses");
int requestLimit = sharedConfig.get("mojang-request-limit");
this.apiConnector = makeApiConnector(getLogger(), ipAddresses, requestLimit);
this.apiConnector = plugin.makeApiConnector(plugin.getLogger(), ipAddresses, requestLimit);
}
public MojangApiConnector getApiConnector() {
@@ -89,20 +136,21 @@ public abstract class FastLoginCore<P> {
return storage;
}
public abstract File getDataFolder();
public T getPlugin() {
return plugin;
}
public abstract Logger getLogger();
public abstract ThreadFactory getThreadFactory();
public void sendLocaleMessage(String key, C receiver) {
String message = localeMessages.get(key);
if (message != null) {
plugin.sendMessage(receiver, message);
}
}
public String getMessage(String key) {
return localeMessages.get(key);
}
public abstract void loadMessages();
public abstract MojangApiConnector makeApiConnector(Logger logger, List<String> addresses, int requests);
public boolean setupDatabase() {
String driver = sharedConfig.get("driver");
String host = sharedConfig.get("host", "");
@@ -117,27 +165,27 @@ public abstract class FastLoginCore<P> {
storage.createTables();
return true;
} catch (Exception ex) {
getLogger().log(Level.SEVERE, "Failed to setup database. Disabling plugin...", ex);
plugin.getLogger().log(Level.SEVERE, "Failed to setup database. Disabling plugin...", ex);
return false;
}
}
public boolean importDatabase(ImportPlugin plugin, boolean sqlite, AuthStorage storage, String host, String database
public boolean importDatabase(ImportPlugin importPlugin, boolean sqlite, AuthStorage storage, String host, String database
, String username, String pass) {
if (sqlite && (plugin == ImportPlugin.BPA || plugin == ImportPlugin.ELDZI)) {
if (sqlite && (importPlugin == ImportPlugin.BPA || importPlugin == ImportPlugin.ELDZI)) {
throw new IllegalArgumentException("These plugins doesn't support flat file databases");
}
Importer importer;
try {
importer = plugin.getImporter().newInstance();
importer = importPlugin.getImporter().newInstance();
} catch (Exception ex) {
getLogger().log(Level.SEVERE, "Couldn't not setup importer class", ex);
plugin.getLogger().log(Level.SEVERE, "Couldn't not setup importer class", ex);
return false;
}
try {
if (sqlite && plugin == ImportPlugin.AUTO_IN) {
if (sqlite && importPlugin == ImportPlugin.AUTO_IN) {
//load sqlite driver
Class.forName("org.sqlite.JDBC");
@@ -154,9 +202,9 @@ public abstract class FastLoginCore<P> {
return true;
}
} catch (ClassNotFoundException ex) {
getLogger().log(Level.SEVERE, "Cannot find SQL driver. Do you removed it?", ex);
plugin.getLogger().log(Level.SEVERE, "Cannot find SQL driver. Do you removed it?", ex);
} catch (SQLException ex) {
getLogger().log(Level.SEVERE, "Couldn't import data. Aborting...", ex);
plugin.getLogger().log(Level.SEVERE, "Couldn't import data. Aborting...", ex);
}
return false;
@@ -190,6 +238,28 @@ public abstract class FastLoginCore<P> {
this.authPlugin = authPlugin;
}
public void saveDefaultFile(String fileName) {
if (!plugin.getDataFolder().exists()) {
plugin.getDataFolder().mkdir();
}
File configFile = new File(plugin.getDataFolder(), fileName);
if (!configFile.exists()) {
InputStream in = getClass().getClassLoader().getResourceAsStream(fileName);
try {
Files.copy(in, configFile.toPath());
} catch (IOException ioExc) {
plugin.getLogger().log(Level.SEVERE, "Error saving default " + fileName, ioExc);
} finally {
try {
in.close();
} catch (IOException ex) {
plugin.getLogger().log(Level.SEVERE, null, ex);
}
}
}
}
public void close() {
if (storage != null) {
storage.close();
@@ -0,0 +1,110 @@
package com.github.games647.fastlogin.core.shared;
import com.github.games647.fastlogin.core.AuthStorage;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import java.util.logging.Level;
public abstract class ForceLoginMangement<P extends C, C, L extends LoginSession, T extends PlatformPlugin<C>>
implements Runnable {
protected final FastLoginCore<P, C, T> core;
protected final P player;
protected L session;
public ForceLoginMangement(FastLoginCore<P, C, T> core, P player) {
this.core = core;
this.player = player;
}
@Override
public void run() {
if (!isOnline(player) || session == null) {
return;
}
AuthStorage storage = core.getStorage();
PlayerProfile playerProfile = session.getProfile();
try {
if (isOnlineMode()) {
//premium player
AuthPlugin<P> authPlugin = core.getAuthPluginHook();
if (authPlugin == null) {
//maybe only bungeecord plugin
onForceActionSuccess(session);
} else {
boolean success = false;
if (isOnline(player)) {
if (core.getConfig().get("autoLogin", true)) {
if (session.needsRegistration()) {
success = forceRegister(player);
} else {
success = forceLogin(player);
}
} else {
success = true;
}
}
if (success) {
//update only on success to prevent corrupt data
if (playerProfile != null) {
playerProfile.setUuid(session.getUuid());
//save cracked players too
playerProfile.setPremium(true);
storage.save(playerProfile);
}
onForceActionSuccess(session);
}
}
} else {
//cracked player
if (playerProfile != null) {
playerProfile.setUuid(null);
playerProfile.setPremium(false);
storage.save(playerProfile);
}
}
} catch (Exception ex) {
core.getPlugin().getLogger().log(Level.INFO, "ERROR ON FORCE LOGIN", ex);
}
}
public boolean forceRegister(P player) {
core.getPlugin().getLogger().log(Level.FINE, "Register player {0}", getName(player));
String generatedPassword = core.getPasswordGenerator().getRandomPassword(player);
boolean success = core.getAuthPluginHook().forceRegister(player, generatedPassword);
String message = core.getMessage("auto-register");
if (success && message != null) {
message = message.replace("%password", generatedPassword);
core.getPlugin().sendMessage(player, message);
}
return success;
}
public boolean forceLogin(P player) {
core.getPlugin().getLogger().log(Level.FINE, "Logging player {0} in", getName(player));
boolean success = core.getAuthPluginHook().forceLogin(player);
if (success) {
core.sendLocaleMessage("auto-login", player);
}
return success;
}
public abstract void onForceActionSuccess(LoginSession session);
public abstract String getName(P player);
public abstract boolean isOnline(P player);
public abstract boolean isOnlineMode();
}
@@ -7,12 +7,12 @@ import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import java.util.UUID;
import java.util.logging.Level;
public abstract class JoinManagement<T, S extends LoginSource> {
public abstract class JoinManagement<P extends C, C, S extends LoginSource> {
protected final FastLoginCore<T> core;
protected final AuthPlugin<T> authHook;
protected final FastLoginCore<P, C, ?> core;
protected final AuthPlugin<P> authHook;
public JoinManagement(FastLoginCore<T> core, AuthPlugin<T> authHook) {
public JoinManagement(FastLoginCore<P, C, ?> core, AuthPlugin<P> authHook) {
this.core = core;
this.authHook = authHook;
}
@@ -30,7 +30,7 @@ public abstract class JoinManagement<T, S extends LoginSource> {
try {
if (profile.getUserId() == -1) {
if (core.getPendingLogins().remove(ip + username) != null && config.get("secondAttemptCracked", false)) {
core.getLogger().log(Level.INFO, "Second attempt login -> cracked {0}", username);
core.getPlugin().getLogger().log(Level.INFO, "Second attempt login -> cracked {0}", username);
//first login request failed so make a cracked session
startCrackedSession(source, profile, username);
@@ -39,7 +39,7 @@ public abstract class JoinManagement<T, S extends LoginSource> {
UUID premiumUUID = null;
if (config.get("nameChangeCheck", false) || config.get("autoRegister", false)) {
core.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
core.getPlugin().getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
premiumUUID = core.getApiConnector().getPremiumUUID(username);
}
@@ -61,7 +61,7 @@ public abstract class JoinManagement<T, S extends LoginSource> {
startCrackedSession(source, profile, username);
}
} catch (Exception ex) {
core.getLogger().log(Level.SEVERE, "Failed to check premium state", ex);
core.getPlugin().getLogger().log(Level.SEVERE, "Failed to check premium state", ex);
}
}
@@ -80,7 +80,7 @@ public abstract class JoinManagement<T, S extends LoginSource> {
PlayerProfile profile = core.getStorage().loadProfile(premiumUUID);
if (profile != null) {
//uuid exists in the database
core.getLogger().log(Level.FINER, "Player {0} changed it's username", premiumUUID);
core.getPlugin().getLogger().log(Level.FINER, "Player {0} changed it's username", premiumUUID);
//update the username to the new one in the database
profile.setPlayerName(username);
@@ -0,0 +1,27 @@
package com.github.games647.fastlogin.core.shared;
import java.io.File;
import java.io.Reader;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Logger;
public interface PlatformPlugin<C> {
String getName();
File getDataFolder();
Logger getLogger();
Map<String, Object> loadYamlFile(Reader reader);
void sendMessage(C receiver, String message);
ThreadFactory getThreadFactory();
String translateColorCodes(char colorChar, String rawMessage);
MojangApiConnector makeApiConnector(Logger logger, List<String> addresses, int requests);
}