forked from TuxCoding/FastLogin
Clean up project structure
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.github.games647</groupId>
|
||||
<artifactId>fastlogin</artifactId>
|
||||
<version>1.4</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>fastlogin.core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>FastLoginCore</name>
|
||||
|
||||
<dependencies>
|
||||
<!--Database pooling-->
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
<version>2.4.6</version>
|
||||
</dependency>
|
||||
|
||||
<!--Logging framework implements slf4j which is required by hikari-->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-jdk14</artifactId>
|
||||
<version>1.7.21</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,59 @@
|
||||
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;
|
||||
|
||||
public abstract class FastLoginCore {
|
||||
|
||||
public static UUID parseId(String withoutDashes) {
|
||||
return UUID.fromString(withoutDashes.substring(0, 8)
|
||||
+ "-" + withoutDashes.substring(8, 12)
|
||||
+ "-" + withoutDashes.substring(12, 16)
|
||||
+ "-" + withoutDashes.substring(16, 20)
|
||||
+ "-" + withoutDashes.substring(20, 32));
|
||||
}
|
||||
|
||||
private MojangApiConnector mojangApiConnector;
|
||||
private Storage storage;
|
||||
|
||||
public void setMojangApiConnector(MojangApiConnector mojangApiConnector) {
|
||||
this.mojangApiConnector = mojangApiConnector;
|
||||
}
|
||||
|
||||
public MojangApiConnector getMojangApiConnector() {
|
||||
return mojangApiConnector;
|
||||
}
|
||||
|
||||
public Storage getStorage() {
|
||||
return storage;
|
||||
}
|
||||
|
||||
public abstract File getDataFolder();
|
||||
|
||||
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) {
|
||||
storage = new Storage(this, driver, host, port, database, user, password);
|
||||
try {
|
||||
storage.createTables();
|
||||
return true;
|
||||
} catch (Exception ex) {
|
||||
getLogger().log(Level.SEVERE, "Failed to setup database. Disabling plugin...", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (storage != null) {
|
||||
storage.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public abstract class MojangApiConnector {
|
||||
|
||||
//http connection, read timeout and user agent for a connection to mojang api servers
|
||||
private static final int TIMEOUT = 1 * 1_000;
|
||||
private static final String USER_AGENT = "Premium-Checker";
|
||||
|
||||
//only premium (paid account) users have a uuid from here
|
||||
private static final String UUID_LINK = "https://api.mojang.com/users/profiles/minecraft/";
|
||||
//this includes a-zA-Z1-9_
|
||||
private static final String VALID_PLAYERNAME = "^\\w{2,16}$";
|
||||
|
||||
//compile the pattern only on plugin enable -> and this have to be threadsafe
|
||||
private final Pattern playernameMatcher = Pattern.compile(VALID_PLAYERNAME);
|
||||
|
||||
protected final FastLoginCore plugin;
|
||||
|
||||
public MojangApiConnector(FastLoginCore plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param playerName
|
||||
* @return null on non-premium
|
||||
*/
|
||||
public UUID getPremiumUUID(String playerName) {
|
||||
//check if it's a valid playername
|
||||
if (playernameMatcher.matcher(playerName).matches()) {
|
||||
//only make a API call if the name is valid existing mojang account
|
||||
try {
|
||||
HttpURLConnection connection = getConnection(UUID_LINK + playerName);
|
||||
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||
String line = reader.readLine();
|
||||
if (line != null && !line.equals("null")) {
|
||||
return getUUIDFromJson(line);
|
||||
}
|
||||
}
|
||||
//204 - no content for not found
|
||||
} catch (Exception ex) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Failed to check if player has a paid account", ex);
|
||||
}
|
||||
//this connection doesn't need to be closed. So can make use of keep alive in java
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract boolean hasJoinedServer(Object session, String serverId);
|
||||
|
||||
protected abstract UUID getUUIDFromJson(String json);
|
||||
|
||||
protected HttpURLConnection getConnection(String url) throws IOException {
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
|
||||
connection.setConnectTimeout(TIMEOUT);
|
||||
connection.setReadTimeout(2 * TIMEOUT);
|
||||
//the new Mojang API just uses json as response
|
||||
connection.setRequestProperty("Content-Type", "application/json");
|
||||
connection.setRequestProperty("User-Agent", USER_AGENT);
|
||||
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class PlayerProfile {
|
||||
|
||||
private final String playerName;
|
||||
|
||||
private long userId;
|
||||
|
||||
private UUID uuid;
|
||||
private boolean premium;
|
||||
private String lastIp;
|
||||
private long lastLogin;
|
||||
|
||||
public PlayerProfile(long userId, UUID uuid, String playerName, boolean premium
|
||||
, String lastIp, long lastLogin) {
|
||||
this.userId = userId;
|
||||
this.uuid = uuid;
|
||||
this.playerName = playerName;
|
||||
this.premium = premium;
|
||||
this.lastIp = lastIp;
|
||||
this.lastLogin = lastLogin;
|
||||
}
|
||||
|
||||
public PlayerProfile(UUID uuid, String playerName, boolean premium, String lastIp) {
|
||||
this.userId = -1;
|
||||
|
||||
this.uuid = uuid;
|
||||
this.playerName = playerName;
|
||||
this.premium = premium;
|
||||
this.lastIp = lastIp;
|
||||
}
|
||||
|
||||
public String getPlayerName() {
|
||||
return playerName;
|
||||
}
|
||||
|
||||
public synchronized long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public synchronized void setUserId(long generatedId) {
|
||||
this.userId = generatedId;
|
||||
}
|
||||
|
||||
public synchronized UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public synchronized void setUuid(UUID uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public synchronized boolean isPremium() {
|
||||
return premium;
|
||||
}
|
||||
|
||||
public synchronized void setPremium(boolean premium) {
|
||||
this.premium = premium;
|
||||
}
|
||||
|
||||
public synchronized String getLastIp() {
|
||||
return lastIp;
|
||||
}
|
||||
|
||||
public synchronized void setLastIp(String lastIp) {
|
||||
this.lastIp = lastIp;
|
||||
}
|
||||
|
||||
public synchronized long getLastLogin() {
|
||||
return lastLogin;
|
||||
}
|
||||
|
||||
public synchronized void setLastLogin(long lastLogin) {
|
||||
this.lastLogin = lastLogin;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
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 {
|
||||
|
||||
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);
|
||||
databaseConfig.setPassword(pass);
|
||||
databaseConfig.setDriverClassName(driver);
|
||||
databaseConfig.setThreadFactory(core.getThreadFactory());
|
||||
|
||||
databasePath = databasePath.replace("{pluginDir}", core.getDataFolder().getAbsolutePath());
|
||||
|
||||
String jdbcUrl = "jdbc:";
|
||||
if (driver.contains("sqlite")) {
|
||||
jdbcUrl += "sqlite" + "://" + databasePath;
|
||||
databaseConfig.setConnectionTestQuery("SELECT 1");
|
||||
} else {
|
||||
jdbcUrl += "mysql" + "://" + host + ':' + port + '/' + databasePath;
|
||||
}
|
||||
|
||||
databaseConfig.setJdbcUrl(jdbcUrl);
|
||||
this.dataSource = new HikariDataSource(databaseConfig);
|
||||
}
|
||||
|
||||
public void createTables() throws SQLException {
|
||||
Connection con = null;
|
||||
try {
|
||||
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`), "
|
||||
//the premium shouldn't steal the cracked account by changing the name
|
||||
+ "UNIQUE (`Name`) "
|
||||
+ ")";
|
||||
|
||||
if (dataSource.getJdbcUrl().contains("sqlite")) {
|
||||
createDataStmt = createDataStmt.replace("AUTO_INCREMENT", "AUTOINCREMENT");
|
||||
}
|
||||
|
||||
statement.executeUpdate(createDataStmt);
|
||||
} finally {
|
||||
closeQuietly(con);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
} else {
|
||||
PlayerProfile crackedProfile = new PlayerProfile(null, name, false, "");
|
||||
profileCache.put(name, crackedProfile);
|
||||
return crackedProfile;
|
||||
}
|
||||
} catch (SQLException sqlEx) {
|
||||
core.getLogger().log(Level.SEVERE, "Failed to query profile", sqlEx);
|
||||
} finally {
|
||||
closeQuietly(con);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean save(PlayerProfile playerProfile) {
|
||||
Connection con = null;
|
||||
try {
|
||||
con = dataSource.getConnection();
|
||||
|
||||
UUID uuid = playerProfile.getUuid();
|
||||
|
||||
if (playerProfile.getUserId() == -1) {
|
||||
PreparedStatement saveStatement = con.prepareStatement("INSERT INTO " + PREMIUM_TABLE
|
||||
+ " (UUID, Name, Premium, LastIp) VALUES (?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
|
||||
|
||||
if (uuid == null) {
|
||||
saveStatement.setString(1, null);
|
||||
} else {
|
||||
saveStatement.setString(1, uuid.toString().replace("-", ""));
|
||||
}
|
||||
|
||||
saveStatement.setString(2, playerProfile.getPlayerName());
|
||||
saveStatement.setBoolean(3, playerProfile.isPremium());
|
||||
saveStatement.setString(4, playerProfile.getLastIp());
|
||||
saveStatement.execute();
|
||||
|
||||
ResultSet generatedKeys = saveStatement.getGeneratedKeys();
|
||||
if (generatedKeys != null && generatedKeys.next()) {
|
||||
playerProfile.setUserId(generatedKeys.getInt(1));
|
||||
}
|
||||
} else {
|
||||
PreparedStatement saveStatement = con.prepareStatement("UPDATE " + PREMIUM_TABLE
|
||||
+ " SET UUID=?, Name=?, Premium=?, LastIp=?, LastLogin=CURRENT_TIMESTAMP WHERE UserID=?");
|
||||
|
||||
if (uuid == null) {
|
||||
saveStatement.setString(1, null);
|
||||
} else {
|
||||
saveStatement.setString(1, uuid.toString().replace("-", ""));
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (SQLException ex) {
|
||||
core.getLogger().log(Level.SEVERE, "Failed to save playerProfile", ex);
|
||||
} finally {
|
||||
closeQuietly(con);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
dataSource.close();
|
||||
profileCache.clear();
|
||||
}
|
||||
|
||||
private void closeQuietly(Connection con) {
|
||||
if (con != null) {
|
||||
try {
|
||||
con.close();
|
||||
} catch (SQLException sqlEx) {
|
||||
core.getLogger().log(Level.SEVERE, "Failed to close connection", sqlEx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
# FastLogin config
|
||||
# Project site: https://www.spigotmc.org/resources/fastlogin.14153
|
||||
# Source code: https://github.com/games647/FastLogin
|
||||
#
|
||||
# You can access the newest config here:
|
||||
# https://github.com/games647/FastLogin/blob/master/core/src/main/resources/config.yml
|
||||
|
||||
# Request a premium login without forcing the player to type a command
|
||||
#
|
||||
# If you activate autoRegister, this plugin will check/do these points on login:
|
||||
# 1. An existing cracked account shouldn't exist
|
||||
# -> paid accounts cannot steal the existing account of cracked players
|
||||
# - (Already registered players could still use the /premium command to activate premium checks)
|
||||
# 2. Automatically registers an account with a strong random generated password
|
||||
# -> cracked player cannot register an account for the premium player and so cannot the steal the account
|
||||
#
|
||||
# Furthermore the premium player check have to be made based on the player name
|
||||
# This means if a cracked player connects to the server and we request a paid account login from this player
|
||||
# the player just disconnect and sees the message: 'bad login' or 'invalid session'
|
||||
# There is no way to change this message
|
||||
# For more information: https://github.com/games647/FastLogin#why-do-players-have-to-invoke-a-command
|
||||
autoRegister: false
|
||||
|
||||
# If this plugin detected that a player has a premium, it can also set the associated
|
||||
# uuid from that account. So if the players changes their usernames, they will still have
|
||||
# the same playerdata (inventory, permissions, ...)
|
||||
#
|
||||
# Warning: This also means that the UUID will be different if the player is connecting
|
||||
# through a offline mode connection. This **could** cause plugin compatibility issues.
|
||||
#
|
||||
# This is a example and doesn't apply for every plugin.
|
||||
# Example: If you want to ban players who aren't online at the moment, the ban plugin will look
|
||||
# after a offline uuid associated to the player, because the server is in offline mode. Then the premium
|
||||
# players could still join the server, because they have different UUID.
|
||||
#
|
||||
# Moreover you may want to convert the offline UUID to a premium UUID. This will ensure that the player
|
||||
# will have the same inventory, permissions, ... if they switched to premium authentification from offline/cracked
|
||||
# authentification.
|
||||
#
|
||||
# This feature requires Cauldron, Spigot or a fork of Spigot (PaperSpigot, TacoSpigot)
|
||||
premiumUuid: false
|
||||
|
||||
# If your players have a premium account and a skin associated to their account, this plugin
|
||||
# can download the data and set it to the online player.
|
||||
#
|
||||
# Keep in mind that this will only works if the player:
|
||||
# * is the owner of the premium account
|
||||
# * the serverconnection is established through a premium connection (paid account authentification)
|
||||
# * has a skin
|
||||
#
|
||||
# This means this plugin doesn't need to create a new connection to the Mojang servers, because
|
||||
# the skin data is included in the Auth-Verification-Response sent by Mojang. If you want to use for other
|
||||
# players like cracked player, you have to use other plugins.
|
||||
#
|
||||
# If you want to use skins for your cracked player, you need an additional plugin like
|
||||
# ChangeSkin, SkinRestoer, ...
|
||||
forwardSkin: true
|
||||
|
||||
# Database configuration
|
||||
# Recommened is the use of MariaDB (a better version of MySQL)
|
||||
|
||||
# Single file SQLite database
|
||||
driver: org.sqlite.JDBC
|
||||
# File location
|
||||
database: '{pluginDir}/FastLogin.db'
|
||||
|
||||
# MySQL and SQLite
|
||||
#driver: com.mysql.jdbc.Driver
|
||||
#host: localhost
|
||||
#port: 3306
|
||||
#database: fastlogin
|
||||
#username: myUser
|
||||
#password: myPassword
|
||||
@@ -0,0 +1,18 @@
|
||||
# FastLogin localization
|
||||
# Project site: https://www.spigotmc.org/resources/fastlogin.14153
|
||||
# Source code: https://github.com/games647/FastLogin
|
||||
#
|
||||
# You can access the newest locale here:
|
||||
# https://github.com/games647/FastLogin/blob/master/core/src/main/resources/messages.yml
|
||||
|
||||
# ========= Shared (BungeeCord an Bukkit) ============
|
||||
|
||||
|
||||
|
||||
# ========= Bukkit only ================================
|
||||
|
||||
|
||||
|
||||
# ========= Bungee only ================================
|
||||
|
||||
|
||||
Reference in New Issue
Block a user