mirror of
https://github.com/TuxCoding/FastLogin.git
synced 2025-07-30 10:47:33 +02:00
Clean up project structure
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@ -45,4 +45,5 @@ gradle-app.setting
|
|||||||
# Project module targets
|
# Project module targets
|
||||||
bukkit/target
|
bukkit/target
|
||||||
universal/target
|
universal/target
|
||||||
bungee/target
|
bungee/target
|
||||||
|
core/target
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.github.games647</groupId>
|
<groupId>com.github.games647</groupId>
|
||||||
<artifactId>fastlogin</artifactId>
|
<artifactId>fastlogin</artifactId>
|
||||||
<version>1.3.1</version>
|
<version>1.4</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
@ -53,6 +53,13 @@
|
|||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.games647</groupId>
|
||||||
|
<artifactId>fastlogin.core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!--Server API-->
|
<!--Server API-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.spigotmc</groupId>
|
<groupId>org.spigotmc</groupId>
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
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 {
|
||||||
|
|
||||||
|
private final FastLoginBukkit plugin;
|
||||||
|
|
||||||
|
public BukkitCore(FastLoginBukkit plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public File getDataFolder() {
|
||||||
|
return plugin.getDataFolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Logger getLogger() {
|
||||||
|
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();
|
||||||
|
return new ThreadFactoryBuilder()
|
||||||
|
.setNameFormat(pluginName + " Database Pool Thread #%1$d")
|
||||||
|
//Hikari create daemons by default
|
||||||
|
.setDaemon(true)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
package com.github.games647.fastlogin.bukkit;
|
||||||
|
|
||||||
|
import com.github.games647.fastlogin.bukkit.hooks.AuthMeHook;
|
||||||
|
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
|
||||||
|
import com.github.games647.fastlogin.bukkit.hooks.CrazyLoginHook;
|
||||||
|
import com.github.games647.fastlogin.bukkit.hooks.LogItHook;
|
||||||
|
import com.github.games647.fastlogin.bukkit.hooks.LoginSecurityHook;
|
||||||
|
import com.github.games647.fastlogin.bukkit.hooks.UltraAuthHook;
|
||||||
|
import com.github.games647.fastlogin.bukkit.hooks.xAuthHook;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
|
||||||
|
public class DelayedAuthHook implements Runnable {
|
||||||
|
|
||||||
|
private final FastLoginBukkit plugin;
|
||||||
|
|
||||||
|
public DelayedAuthHook(FastLoginBukkit plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
boolean hookFound = registerHooks();
|
||||||
|
if (plugin.isBungeeCord()) {
|
||||||
|
plugin.getLogger().info("BungeeCord setting detected. No auth plugin is required");
|
||||||
|
} else if (!hookFound) {
|
||||||
|
plugin.getLogger().warning("No auth plugin were found by this plugin "
|
||||||
|
+ "(other plugins could hook into this after the intialization of this plugin)"
|
||||||
|
+ "and bungeecord is deactivated. "
|
||||||
|
+ "Either one or both of the checks have to pass in order to use this plugin");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean registerHooks() {
|
||||||
|
BukkitAuthPlugin authPluginHook = null;
|
||||||
|
try {
|
||||||
|
List<Class<? extends BukkitAuthPlugin>> supportedHooks = Lists.newArrayList(AuthMeHook.class
|
||||||
|
, CrazyLoginHook.class, LogItHook.class, LoginSecurityHook.class, UltraAuthHook.class
|
||||||
|
, xAuthHook.class);
|
||||||
|
for (Class<? extends BukkitAuthPlugin> clazz : supportedHooks) {
|
||||||
|
String pluginName = clazz.getSimpleName().replace("Hook", "");
|
||||||
|
//uses only member classes which uses AuthPlugin interface (skip interfaces)
|
||||||
|
if (Bukkit.getServer().getPluginManager().getPlugin(pluginName) != null) {
|
||||||
|
//check only for enabled plugins. A single plugin could be disabled by plugin managers
|
||||||
|
authPluginHook = clazz.newInstance();
|
||||||
|
plugin.getLogger().log(Level.INFO, "Hooking into auth plugin: {0}", pluginName);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InstantiationException | IllegalAccessException ex) {
|
||||||
|
plugin.getLogger().log(Level.SEVERE, "Couldn't load the integration class", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authPluginHook == null) {
|
||||||
|
//run this check for exceptions (errors) and not found plugins
|
||||||
|
plugin.getLogger().warning("No support offline Auth plugin found. ");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plugin.getAuthPlugin() != null) {
|
||||||
|
plugin.setAuthPluginHook(authPluginHook);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -7,29 +7,19 @@ import com.comphenix.protocol.ProtocolManager;
|
|||||||
import com.comphenix.protocol.utility.SafeCacheBuilder;
|
import com.comphenix.protocol.utility.SafeCacheBuilder;
|
||||||
import com.github.games647.fastlogin.bukkit.commands.CrackedCommand;
|
import com.github.games647.fastlogin.bukkit.commands.CrackedCommand;
|
||||||
import com.github.games647.fastlogin.bukkit.commands.PremiumCommand;
|
import com.github.games647.fastlogin.bukkit.commands.PremiumCommand;
|
||||||
import com.github.games647.fastlogin.bukkit.hooks.AuthMeHook;
|
|
||||||
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
|
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
|
||||||
import com.github.games647.fastlogin.bukkit.hooks.CrazyLoginHook;
|
|
||||||
import com.github.games647.fastlogin.bukkit.hooks.LogItHook;
|
|
||||||
import com.github.games647.fastlogin.bukkit.hooks.LoginSecurityHook;
|
|
||||||
import com.github.games647.fastlogin.bukkit.hooks.UltraAuthHook;
|
|
||||||
import com.github.games647.fastlogin.bukkit.hooks.xAuthHook;
|
|
||||||
import com.github.games647.fastlogin.bukkit.listener.BukkitJoinListener;
|
import com.github.games647.fastlogin.bukkit.listener.BukkitJoinListener;
|
||||||
import com.github.games647.fastlogin.bukkit.listener.BungeeCordListener;
|
import com.github.games647.fastlogin.bukkit.listener.BungeeCordListener;
|
||||||
import com.github.games647.fastlogin.bukkit.listener.EncryptionPacketListener;
|
|
||||||
import com.github.games647.fastlogin.bukkit.listener.ProtocolSupportListener;
|
import com.github.games647.fastlogin.bukkit.listener.ProtocolSupportListener;
|
||||||
import com.github.games647.fastlogin.bukkit.listener.StartPacketListener;
|
import com.github.games647.fastlogin.bukkit.listener.packet.EncryptionPacketListener;
|
||||||
|
import com.github.games647.fastlogin.bukkit.listener.packet.StartPacketListener;
|
||||||
|
import com.github.games647.fastlogin.core.FastLoginCore;
|
||||||
import com.google.common.cache.CacheLoader;
|
import com.google.common.cache.CacheLoader;
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
|
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
@ -41,19 +31,11 @@ public class FastLoginBukkit extends JavaPlugin {
|
|||||||
|
|
||||||
private static final int WORKER_THREADS = 5;
|
private static final int WORKER_THREADS = 5;
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
//provide a immutable key pair to be thread safe | used for encrypting and decrypting traffic
|
//provide a immutable key pair to be thread safe | used for encrypting and decrypting traffic
|
||||||
private final KeyPair keyPair = EncryptionUtil.generateKeyPair();
|
private final KeyPair keyPair = EncryptionUtil.generateKeyPair();
|
||||||
|
|
||||||
protected boolean bungeeCord;
|
private boolean bungeeCord;
|
||||||
private Storage storage;
|
private final FastLoginCore core = new BukkitCore(this);
|
||||||
private boolean serverStarted;
|
private boolean serverStarted;
|
||||||
|
|
||||||
//this map is thread-safe for async access (Packet Listener)
|
//this map is thread-safe for async access (Packet Listener)
|
||||||
@ -72,11 +54,12 @@ public class FastLoginBukkit extends JavaPlugin {
|
|||||||
});
|
});
|
||||||
|
|
||||||
private BukkitAuthPlugin authPlugin;
|
private BukkitAuthPlugin authPlugin;
|
||||||
private final MojangApiConnector mojangApiConnector = new MojangApiConnector(this);
|
|
||||||
private PasswordGenerator passwordGenerator = new DefaultPasswordGenerator();
|
private PasswordGenerator passwordGenerator = new DefaultPasswordGenerator();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
|
core.setMojangApiConnector(new MojangApiBukkit(core));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (ClassUtil.isPresent("org.spigotmc.SpigotConfig")) {
|
if (ClassUtil.isPresent("org.spigotmc.SpigotConfig")) {
|
||||||
bungeeCord = Class.forName("org.spigotmc.SpigotConfig").getDeclaredField("bungee").getBoolean(null);
|
bungeeCord = Class.forName("org.spigotmc.SpigotConfig").getDeclaredField("bungee").getBoolean(null);
|
||||||
@ -111,11 +94,7 @@ public class FastLoginBukkit extends JavaPlugin {
|
|||||||
String username = getConfig().getString("username", "");
|
String username = getConfig().getString("username", "");
|
||||||
String password = getConfig().getString("password", "");
|
String password = getConfig().getString("password", "");
|
||||||
|
|
||||||
this.storage = new Storage(this, driver, host, port, database, username, password);
|
if (!core.setupDatabase(driver, host, port, database, username, password)) {
|
||||||
try {
|
|
||||||
storage.createTables();
|
|
||||||
} catch (SQLException sqlEx) {
|
|
||||||
getLogger().log(Level.SEVERE, "Failed to create database tables. Disabling plugin...", sqlEx);
|
|
||||||
setEnabled(false);
|
setEnabled(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -137,20 +116,7 @@ public class FastLoginBukkit extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//delay dependency setup because we load the plugin very early where plugins are initialized yet
|
//delay dependency setup because we load the plugin very early where plugins are initialized yet
|
||||||
getServer().getScheduler().runTask(this, new Runnable() {
|
getServer().getScheduler().runTask(this, new DelayedAuthHook(this));
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
boolean hookFound = registerHooks();
|
|
||||||
if (bungeeCord) {
|
|
||||||
getLogger().info("BungeeCord setting detected. No auth plugin is required");
|
|
||||||
} else if (!hookFound) {
|
|
||||||
getLogger().warning("No auth plugin were found by this plugin "
|
|
||||||
+ "(other plugins could hook into this after the intialization of this plugin)"
|
|
||||||
+ "and bungeecord is deactivated. "
|
|
||||||
+ "Either one or both of the checks have to pass in order to use this plugin");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
getServer().getPluginManager().registerEvents(new BukkitJoinListener(this), this);
|
getServer().getPluginManager().registerEvents(new BukkitJoinListener(this), this);
|
||||||
|
|
||||||
@ -169,11 +135,15 @@ public class FastLoginBukkit extends JavaPlugin {
|
|||||||
player.removeMetadata(getName(), this);
|
player.removeMetadata(getName(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (storage != null) {
|
if (core != null) {
|
||||||
storage.close();
|
core.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FastLoginCore getCore() {
|
||||||
|
return core;
|
||||||
|
}
|
||||||
|
|
||||||
public String generateStringPassword(Player player) {
|
public String generateStringPassword(Player player) {
|
||||||
return passwordGenerator.getRandomPassword(player);
|
return passwordGenerator.getRandomPassword(player);
|
||||||
}
|
}
|
||||||
@ -201,10 +171,6 @@ public class FastLoginBukkit extends JavaPlugin {
|
|||||||
return keyPair;
|
return keyPair;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Storage getStorage() {
|
|
||||||
return storage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the auth plugin hook in order to interact with the plugins. This can be null if no supporting auth plugin
|
* Gets the auth plugin hook in order to interact with the plugins. This can be null if no supporting auth plugin
|
||||||
* was found.
|
* was found.
|
||||||
@ -216,7 +182,7 @@ public class FastLoginBukkit extends JavaPlugin {
|
|||||||
try {
|
try {
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
} catch (InterruptedException ex) {
|
} catch (InterruptedException ex) {
|
||||||
Logger.getLogger(FastLoginBukkit.class.getName()).log(Level.SEVERE, null, ex);
|
getLogger().log(Level.SEVERE, null, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,45 +193,6 @@ public class FastLoginBukkit extends JavaPlugin {
|
|||||||
this.authPlugin = authPlugin;
|
this.authPlugin = authPlugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the a connection in order to access important features from the Mojang API.
|
|
||||||
*
|
|
||||||
* @return the connector instance
|
|
||||||
*/
|
|
||||||
public MojangApiConnector getApiConnector() {
|
|
||||||
return mojangApiConnector;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean registerHooks() {
|
|
||||||
BukkitAuthPlugin authPluginHook = null;
|
|
||||||
try {
|
|
||||||
List<Class<? extends BukkitAuthPlugin>> supportedHooks = Lists.newArrayList(AuthMeHook.class
|
|
||||||
, CrazyLoginHook.class, LogItHook.class, LoginSecurityHook.class, UltraAuthHook.class
|
|
||||||
, xAuthHook.class);
|
|
||||||
for (Class<? extends BukkitAuthPlugin> clazz : supportedHooks) {
|
|
||||||
String pluginName = clazz.getSimpleName().replace("Hook", "");
|
|
||||||
//uses only member classes which uses AuthPlugin interface (skip interfaces)
|
|
||||||
if (getServer().getPluginManager().getPlugin(pluginName) != null) {
|
|
||||||
//check only for enabled plugins. A single plugin could be disabled by plugin managers
|
|
||||||
authPluginHook = clazz.newInstance();
|
|
||||||
getLogger().log(Level.INFO, "Hooking into auth plugin: {0}", pluginName);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (InstantiationException | IllegalAccessException ex) {
|
|
||||||
getLogger().log(Level.SEVERE, "Couldn't load the integration class", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (authPluginHook == null) {
|
|
||||||
//run this check for exceptions (errors) and not found plugins
|
|
||||||
getLogger().warning("No support offline Auth plugin found. ");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
authPlugin = authPluginHook;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isBungeeCord() {
|
public boolean isBungeeCord() {
|
||||||
return bungeeCord;
|
return bungeeCord;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package com.github.games647.fastlogin.bukkit;
|
package com.github.games647.fastlogin.bukkit;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
|
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
|
||||||
|
import com.github.games647.fastlogin.core.PlayerProfile;
|
||||||
|
import com.github.games647.fastlogin.core.Storage;
|
||||||
import com.google.common.io.ByteArrayDataOutput;
|
import com.google.common.io.ByteArrayDataOutput;
|
||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
|
|
||||||
@ -35,7 +37,7 @@ public class ForceLoginTask implements Runnable {
|
|||||||
|
|
||||||
BukkitAuthPlugin authPlugin = plugin.getAuthPlugin();
|
BukkitAuthPlugin authPlugin = plugin.getAuthPlugin();
|
||||||
|
|
||||||
Storage storage = plugin.getStorage();
|
Storage storage = plugin.getCore().getStorage();
|
||||||
PlayerProfile playerProfile = null;
|
PlayerProfile playerProfile = null;
|
||||||
if (storage != null) {
|
if (storage != null) {
|
||||||
playerProfile = storage.getProfile(player.getName(), false);
|
playerProfile = storage.getProfile(player.getName(), false);
|
||||||
|
@ -0,0 +1,73 @@
|
|||||||
|
package com.github.games647.fastlogin.bukkit;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.wrappers.WrappedSignedProperty;
|
||||||
|
import com.github.games647.fastlogin.core.FastLoginCore;
|
||||||
|
import com.github.games647.fastlogin.core.MojangApiConnector;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import org.json.simple.JSONArray;
|
||||||
|
import org.json.simple.JSONObject;
|
||||||
|
import org.json.simple.JSONValue;
|
||||||
|
|
||||||
|
public class MojangApiBukkit extends MojangApiConnector {
|
||||||
|
|
||||||
|
//mojang api check to prove a player is logged in minecraft and made a join server request
|
||||||
|
private static final String HAS_JOINED_URL = "https://sessionserver.mojang.com/session/minecraft/hasJoined?";
|
||||||
|
|
||||||
|
public MojangApiBukkit(FastLoginCore plugin) {
|
||||||
|
super(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasJoinedServer(Object session, String serverId) {
|
||||||
|
if (!(session instanceof PlayerSession)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerSession playerSession = (PlayerSession) session;
|
||||||
|
try {
|
||||||
|
String url = HAS_JOINED_URL + "username=" + playerSession.getUsername() + "&serverId=" + serverId;
|
||||||
|
HttpURLConnection conn = getConnection(url);
|
||||||
|
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||||
|
String line = reader.readLine();
|
||||||
|
if (line != null && !line.equals("null")) {
|
||||||
|
//validate parsing
|
||||||
|
//http://wiki.vg/Protocol_Encryption#Server
|
||||||
|
JSONObject userData = (JSONObject) JSONValue.parseWithException(line);
|
||||||
|
String uuid = (String) userData.get("id");
|
||||||
|
playerSession.setUuid(FastLoginCore.parseId(uuid));
|
||||||
|
|
||||||
|
JSONArray properties = (JSONArray) userData.get("properties");
|
||||||
|
JSONObject skinProperty = (JSONObject) properties.get(0);
|
||||||
|
|
||||||
|
String propertyName = (String) skinProperty.get("name");
|
||||||
|
if (propertyName.equals("textures")) {
|
||||||
|
String skinValue = (String) skinProperty.get("value");
|
||||||
|
String signature = (String) skinProperty.get("signature");
|
||||||
|
playerSession.setSkin(WrappedSignedProperty.fromValues(propertyName, skinValue, signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
//catch not only ioexceptions also parse and NPE on unexpected json format
|
||||||
|
plugin.getLogger().log(Level.WARNING, "Failed to verify session", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
//this connection doesn't need to be closed. So can make use of keep alive in java
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected UUID getUUIDFromJson(String json) {
|
||||||
|
JSONObject userData = (JSONObject) JSONValue.parse(json);
|
||||||
|
String uuid = (String) userData.get("id");
|
||||||
|
return FastLoginCore.parseId(uuid);
|
||||||
|
}
|
||||||
|
}
|
@ -1,124 +0,0 @@
|
|||||||
package com.github.games647.fastlogin.bukkit;
|
|
||||||
|
|
||||||
import com.comphenix.protocol.wrappers.WrappedSignedProperty;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
import org.json.simple.JSONArray;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
import org.json.simple.JSONValue;
|
|
||||||
|
|
||||||
public 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";
|
|
||||||
|
|
||||||
//mojang api check to prove a player is logged in minecraft and made a join server request
|
|
||||||
private static final String HAS_JOINED_URL = "https://sessionserver.mojang.com/session/minecraft/hasJoined?";
|
|
||||||
|
|
||||||
//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);
|
|
||||||
|
|
||||||
private final FastLoginBukkit plugin;
|
|
||||||
|
|
||||||
public MojangApiConnector(FastLoginBukkit 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")) {
|
|
||||||
JSONObject userData = (JSONObject) JSONValue.parseWithException(line);
|
|
||||||
String uuid = (String) userData.get("id");
|
|
||||||
return parseId(uuid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//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 boolean hasJoinedServer(PlayerSession session, String serverId) {
|
|
||||||
try {
|
|
||||||
String url = HAS_JOINED_URL + "username=" + session.getUsername() + "&serverId=" + serverId;
|
|
||||||
HttpURLConnection conn = getConnection(url);
|
|
||||||
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
|
||||||
String line = reader.readLine();
|
|
||||||
if (line != null && !line.equals("null")) {
|
|
||||||
//validate parsing
|
|
||||||
//http://wiki.vg/Protocol_Encryption#Server
|
|
||||||
JSONObject userData = (JSONObject) JSONValue.parseWithException(line);
|
|
||||||
String uuid = (String) userData.get("id");
|
|
||||||
session.setUuid(parseId(uuid));
|
|
||||||
|
|
||||||
JSONArray properties = (JSONArray) userData.get("properties");
|
|
||||||
JSONObject skinProperty = (JSONObject) properties.get(0);
|
|
||||||
|
|
||||||
String propertyName = (String) skinProperty.get("name");
|
|
||||||
if (propertyName.equals("textures")) {
|
|
||||||
String skinValue = (String) skinProperty.get("value");
|
|
||||||
String signature = (String) skinProperty.get("signature");
|
|
||||||
session.setSkin(WrappedSignedProperty.fromValues(propertyName, skinValue, signature));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
//catch not only ioexceptions also parse and NPE on unexpected json format
|
|
||||||
plugin.getLogger().log(Level.WARNING, "Failed to verify session", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
//this connection doesn't need to be closed. So can make use of keep alive in java
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private 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 HttpURLConnection getConnection(String url) throws IOException {
|
|
||||||
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
|
|
||||||
connection.setConnectTimeout(TIMEOUT);
|
|
||||||
connection.setReadTimeout(TIMEOUT);
|
|
||||||
//the new Mojang API just uses json as response
|
|
||||||
connection.setRequestProperty("Content-Type", "application/json");
|
|
||||||
connection.setRequestProperty("User-Agent", USER_AGENT);
|
|
||||||
|
|
||||||
return connection;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
package com.github.games647.fastlogin.bukkit;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected 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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
package com.github.games647.fastlogin.bukkit.commands;
|
package com.github.games647.fastlogin.bukkit.commands;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
import com.github.games647.fastlogin.bukkit.PlayerProfile;
|
import com.github.games647.fastlogin.core.PlayerProfile;
|
||||||
import com.google.common.io.ByteArrayDataOutput;
|
import com.google.common.io.ByteArrayDataOutput;
|
||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ public class CrackedCommand implements CommandExecutor {
|
|||||||
sender.sendMessage(ChatColor.YELLOW + "Sending request...");
|
sender.sendMessage(ChatColor.YELLOW + "Sending request...");
|
||||||
} else {
|
} else {
|
||||||
//todo: load async if it's not in the cache anymore
|
//todo: load async if it's not in the cache anymore
|
||||||
final PlayerProfile profile = plugin.getStorage().getProfile(sender.getName(), true);
|
final PlayerProfile profile = plugin.getCore().getStorage().getProfile(sender.getName(), true);
|
||||||
if (profile.isPremium()) {
|
if (profile.isPremium()) {
|
||||||
sender.sendMessage(ChatColor.DARK_GREEN + "Removed from the list of premium players");
|
sender.sendMessage(ChatColor.DARK_GREEN + "Removed from the list of premium players");
|
||||||
profile.setPremium(false);
|
profile.setPremium(false);
|
||||||
@ -42,7 +42,7 @@ public class CrackedCommand implements CommandExecutor {
|
|||||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
plugin.getStorage().save(profile);
|
plugin.getCore().getStorage().save(profile);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -62,7 +62,7 @@ public class CrackedCommand implements CommandExecutor {
|
|||||||
sender.sendMessage(ChatColor.YELLOW + "Sending request for player " + args[0] + "...");
|
sender.sendMessage(ChatColor.YELLOW + "Sending request for player " + args[0] + "...");
|
||||||
} else {
|
} else {
|
||||||
//todo: load async if it's not in the cache anymore
|
//todo: load async if it's not in the cache anymore
|
||||||
final PlayerProfile profile = plugin.getStorage().getProfile(args[0], true);
|
final PlayerProfile profile = plugin.getCore().getStorage().getProfile(args[0], true);
|
||||||
if (profile == null) {
|
if (profile == null) {
|
||||||
sender.sendMessage(ChatColor.DARK_RED + "Player not in the database");
|
sender.sendMessage(ChatColor.DARK_RED + "Player not in the database");
|
||||||
return true;
|
return true;
|
||||||
@ -75,7 +75,7 @@ public class CrackedCommand implements CommandExecutor {
|
|||||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
plugin.getStorage().save(profile);
|
plugin.getCore().getStorage().save(profile);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package com.github.games647.fastlogin.bukkit.commands;
|
package com.github.games647.fastlogin.bukkit.commands;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
import com.github.games647.fastlogin.bukkit.PlayerProfile;
|
import com.github.games647.fastlogin.core.PlayerProfile;
|
||||||
import com.google.common.io.ByteArrayDataOutput;
|
import com.google.common.io.ByteArrayDataOutput;
|
||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ public class PremiumCommand implements CommandExecutor {
|
|||||||
sender.sendMessage(ChatColor.YELLOW + "Sending request...");
|
sender.sendMessage(ChatColor.YELLOW + "Sending request...");
|
||||||
} else {
|
} else {
|
||||||
// //todo: load async if it's not in the cache anymore
|
// //todo: load async if it's not in the cache anymore
|
||||||
final PlayerProfile profile = plugin.getStorage().getProfile(sender.getName(), true);
|
final PlayerProfile profile = plugin.getCore().getStorage().getProfile(sender.getName(), true);
|
||||||
if (profile.isPremium()) {
|
if (profile.isPremium()) {
|
||||||
sender.sendMessage(ChatColor.DARK_RED + "You are already on the premium list");
|
sender.sendMessage(ChatColor.DARK_RED + "You are already on the premium list");
|
||||||
} else {
|
} else {
|
||||||
@ -48,7 +48,7 @@ public class PremiumCommand implements CommandExecutor {
|
|||||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
plugin.getStorage().save(profile);
|
plugin.getCore().getStorage().save(profile);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ public class PremiumCommand implements CommandExecutor {
|
|||||||
sender.sendMessage(ChatColor.YELLOW + "Sending request...");
|
sender.sendMessage(ChatColor.YELLOW + "Sending request...");
|
||||||
} else {
|
} else {
|
||||||
//todo: load async if it's not in the cache anymore
|
//todo: load async if it's not in the cache anymore
|
||||||
final PlayerProfile profile = plugin.getStorage().getProfile(args[0], true);
|
final PlayerProfile profile = plugin.getCore().getStorage().getProfile(args[0], true);
|
||||||
if (profile == null) {
|
if (profile == null) {
|
||||||
sender.sendMessage(ChatColor.DARK_RED + "Player not in the database");
|
sender.sendMessage(ChatColor.DARK_RED + "Player not in the database");
|
||||||
return true;
|
return true;
|
||||||
@ -82,7 +82,7 @@ public class PremiumCommand implements CommandExecutor {
|
|||||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
plugin.getStorage().save(profile);
|
plugin.getCore().getStorage().save(profile);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -30,9 +30,7 @@ public class AuthMeHook implements BukkitAuthPlugin {
|
|||||||
} else {
|
} else {
|
||||||
API.forceLogin(player);
|
API.forceLogin(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
//commented because the operation above is performed async -> race conditions
|
|
||||||
// return NewAPI.getInstance().isAuthenticated(player);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +41,6 @@ public class BukkitJoinListener implements Listener {
|
|||||||
public void onPlayerJoin(PlayerJoinEvent joinEvent) {
|
public void onPlayerJoin(PlayerJoinEvent joinEvent) {
|
||||||
Player player = joinEvent.getPlayer();
|
Player player = joinEvent.getPlayer();
|
||||||
|
|
||||||
//removing the session because we now use it
|
|
||||||
PlayerSession session = plugin.getSessions().get(player.getAddress().toString());
|
PlayerSession session = plugin.getSessions().get(player.getAddress().toString());
|
||||||
if (session != null && plugin.getConfig().getBoolean("forwardSkin")) {
|
if (session != null && plugin.getConfig().getBoolean("forwardSkin")) {
|
||||||
WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(player);
|
WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(player);
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package com.github.games647.fastlogin.bukkit.listener;
|
package com.github.games647.fastlogin.bukkit.listener;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
import com.github.games647.fastlogin.bukkit.PlayerProfile;
|
|
||||||
import com.github.games647.fastlogin.bukkit.PlayerSession;
|
import com.github.games647.fastlogin.bukkit.PlayerSession;
|
||||||
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
|
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
|
||||||
|
import com.github.games647.fastlogin.core.PlayerProfile;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -40,7 +40,7 @@ public class ProtocolSupportListener implements Listener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerProfile playerProfile = plugin.getStorage().getProfile(username, true);
|
PlayerProfile playerProfile = plugin.getCore().getStorage().getProfile(username, true);
|
||||||
if (playerProfile != null) {
|
if (playerProfile != null) {
|
||||||
if (playerProfile.isPremium()) {
|
if (playerProfile.isPremium()) {
|
||||||
if (playerProfile.getUserId() != -1) {
|
if (playerProfile.getUserId() != -1) {
|
||||||
@ -50,7 +50,7 @@ public class ProtocolSupportListener implements Listener {
|
|||||||
//user not exists in the db
|
//user not exists in the db
|
||||||
try {
|
try {
|
||||||
if (plugin.getConfig().getBoolean("autoRegister") && !authPlugin.isRegistered(username)) {
|
if (plugin.getConfig().getBoolean("autoRegister") && !authPlugin.isRegistered(username)) {
|
||||||
UUID premiumUUID = plugin.getApiConnector().getPremiumUUID(username);
|
UUID premiumUUID = plugin.getCore().getMojangApiConnector().getPremiumUUID(username);
|
||||||
if (premiumUUID != null) {
|
if (premiumUUID != null) {
|
||||||
plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
|
plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
|
||||||
startPremiumSession(username, loginStartEvent, false);
|
startPremiumSession(username, loginStartEvent, false);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package com.github.games647.fastlogin.bukkit.listener;
|
package com.github.games647.fastlogin.bukkit.listener.packet;
|
||||||
|
|
||||||
import com.comphenix.protocol.PacketType;
|
import com.comphenix.protocol.PacketType;
|
||||||
import com.comphenix.protocol.ProtocolManager;
|
import com.comphenix.protocol.ProtocolManager;
|
||||||
@ -71,7 +71,6 @@ public class EncryptionPacketListener extends PacketAdapter {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onPacketReceiving(PacketEvent packetEvent) {
|
public void onPacketReceiving(PacketEvent packetEvent) {
|
||||||
System.out.println("ENCRYPTION REQUEST");
|
|
||||||
Player player = packetEvent.getPlayer();
|
Player player = packetEvent.getPlayer();
|
||||||
|
|
||||||
//the player name is unknown to ProtocolLib (so getName() doesn't work) - now uses ip:port as key
|
//the player name is unknown to ProtocolLib (so getName() doesn't work) - now uses ip:port as key
|
||||||
@ -102,7 +101,7 @@ public class EncryptionPacketListener extends PacketAdapter {
|
|||||||
String serverId = (new BigInteger(serverIdHash)).toString(16);
|
String serverId = (new BigInteger(serverIdHash)).toString(16);
|
||||||
|
|
||||||
String username = session.getUsername();
|
String username = session.getUsername();
|
||||||
if (plugin.getApiConnector().hasJoinedServer(session, serverId)) {
|
if (plugin.getCore().getMojangApiConnector().hasJoinedServer(session, serverId)) {
|
||||||
plugin.getLogger().log(Level.FINE, "Player {0} has a verified premium account", username);
|
plugin.getLogger().log(Level.FINE, "Player {0} has a verified premium account", username);
|
||||||
|
|
||||||
session.setVerified(true);
|
session.setVerified(true);
|
@ -1,4 +1,4 @@
|
|||||||
package com.github.games647.fastlogin.bukkit.listener;
|
package com.github.games647.fastlogin.bukkit.listener.packet;
|
||||||
|
|
||||||
import com.comphenix.protocol.PacketType;
|
import com.comphenix.protocol.PacketType;
|
||||||
import com.comphenix.protocol.ProtocolManager;
|
import com.comphenix.protocol.ProtocolManager;
|
||||||
@ -6,7 +6,7 @@ import com.comphenix.protocol.events.PacketAdapter;
|
|||||||
import com.comphenix.protocol.events.PacketContainer;
|
import com.comphenix.protocol.events.PacketContainer;
|
||||||
import com.comphenix.protocol.events.PacketEvent;
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||||
import com.github.games647.fastlogin.bukkit.PlayerProfile;
|
import com.github.games647.fastlogin.core.PlayerProfile;
|
||||||
import com.github.games647.fastlogin.bukkit.PlayerSession;
|
import com.github.games647.fastlogin.bukkit.PlayerSession;
|
||||||
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
|
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ public class StartPacketListener extends PacketAdapter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerProfile playerProfile = plugin.getStorage().getProfile(username, true);
|
PlayerProfile playerProfile = plugin.getCore().getStorage().getProfile(username, true);
|
||||||
if (playerProfile != null) {
|
if (playerProfile != null) {
|
||||||
if (playerProfile.isPremium()) {
|
if (playerProfile.isPremium()) {
|
||||||
if (playerProfile.getUserId() != -1) {
|
if (playerProfile.getUserId() != -1) {
|
||||||
@ -93,7 +93,7 @@ public class StartPacketListener extends PacketAdapter {
|
|||||||
//user not exists in the db
|
//user not exists in the db
|
||||||
try {
|
try {
|
||||||
if (plugin.getConfig().getBoolean("autoRegister") && !authPlugin.isRegistered(username)) {
|
if (plugin.getConfig().getBoolean("autoRegister") && !authPlugin.isRegistered(username)) {
|
||||||
UUID premiumUUID = plugin.getApiConnector().getPremiumUUID(username);
|
UUID premiumUUID = plugin.getCore().getMojangApiConnector().getPremiumUUID(username);
|
||||||
if (premiumUUID != null) {
|
if (premiumUUID != null) {
|
||||||
plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
|
plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
|
||||||
enablePremiumLogin(username, sessionKey, player, packetEvent, false);
|
enablePremiumLogin(username, sessionKey, player, packetEvent, false);
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.github.games647</groupId>
|
<groupId>com.github.games647</groupId>
|
||||||
<artifactId>fastlogin</artifactId>
|
<artifactId>fastlogin</artifactId>
|
||||||
<version>1.3.1</version>
|
<version>1.4</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
@ -37,6 +37,13 @@
|
|||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>fastlogin.core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.md-5</groupId>
|
<groupId>net.md-5</groupId>
|
||||||
<artifactId>bungeecord-proxy</artifactId>
|
<artifactId>bungeecord-proxy</artifactId>
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
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.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;
|
||||||
|
|
||||||
|
public class AsyncPremiumCheck implements Runnable {
|
||||||
|
|
||||||
|
private final FastLoginBungee plugin;
|
||||||
|
private final PreLoginEvent preLoginEvent;
|
||||||
|
|
||||||
|
public AsyncPremiumCheck(FastLoginBungee plugin, PreLoginEvent preLoginEvent) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.preLoginEvent = preLoginEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
PendingConnection connection = preLoginEvent.getConnection();
|
||||||
|
String username = connection.getName();
|
||||||
|
try {
|
||||||
|
PlayerProfile playerProfile = plugin.getCore().getStorage().getProfile(username, true);
|
||||||
|
if (playerProfile != null) {
|
||||||
|
if (playerProfile.isPremium()) {
|
||||||
|
if (playerProfile.getUserId() != -1) {
|
||||||
|
connection.setOnlineMode(true);
|
||||||
|
}
|
||||||
|
} else if (playerProfile.getUserId() == -1) {
|
||||||
|
//user not exists in the db
|
||||||
|
BungeeAuthPlugin authPlugin = plugin.getBungeeAuthPlugin();
|
||||||
|
if (plugin.getConfiguration().getBoolean("autoRegister")
|
||||||
|
&& (authPlugin == null || !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);
|
||||||
|
connection.setOnlineMode(true);
|
||||||
|
plugin.getPendingAutoRegister().put(connection, new Object());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
plugin.getLogger().log(Level.SEVERE, "Failed to check premium state", ex);
|
||||||
|
} finally {
|
||||||
|
preLoginEvent.completeIntent(plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
public class BungeeCore extends FastLoginCore {
|
||||||
|
|
||||||
|
private final FastLoginBungee plugin;
|
||||||
|
|
||||||
|
public BungeeCore(FastLoginBungee plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public File getDataFolder() {
|
||||||
|
return plugin.getDataFolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Logger getLogger() {
|
||||||
|
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();
|
||||||
|
return new ThreadFactoryBuilder()
|
||||||
|
.setNameFormat(pluginName + " Database Pool Thread #%1$d")
|
||||||
|
//Hikari create daemons by default
|
||||||
|
.setDaemon(true)
|
||||||
|
.setThreadFactory(new GroupedThreadFactory(plugin, pluginName)).build();
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,10 @@
|
|||||||
package com.github.games647.fastlogin.bungee;
|
package com.github.games647.fastlogin.bungee;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bungee.listener.PlayerConnectionListener;
|
|
||||||
import com.github.games647.fastlogin.bungee.hooks.BungeeAuthHook;
|
import com.github.games647.fastlogin.bungee.hooks.BungeeAuthHook;
|
||||||
import com.github.games647.fastlogin.bungee.hooks.BungeeAuthPlugin;
|
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.bungee.listener.PluginMessageListener;
|
||||||
|
import com.github.games647.fastlogin.core.FastLoginCore;
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -11,12 +12,10 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import net.md_5.bungee.Util;
|
|
||||||
import net.md_5.bungee.api.connection.PendingConnection;
|
import net.md_5.bungee.api.connection.PendingConnection;
|
||||||
import net.md_5.bungee.api.plugin.Plugin;
|
import net.md_5.bungee.api.plugin.Plugin;
|
||||||
import net.md_5.bungee.config.Configuration;
|
import net.md_5.bungee.config.Configuration;
|
||||||
@ -28,16 +27,11 @@ import net.md_5.bungee.config.YamlConfiguration;
|
|||||||
*/
|
*/
|
||||||
public class FastLoginBungee extends Plugin {
|
public class FastLoginBungee extends Plugin {
|
||||||
|
|
||||||
private static final char[] CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
private static final char[] PASSWORD_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||||
.toCharArray();
|
.toCharArray();
|
||||||
|
|
||||||
public static UUID parseId(String withoutDashes) {
|
private final FastLoginCore loginCore = new BungeeCore(this);
|
||||||
return Util.getUUID(withoutDashes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private BungeeAuthPlugin bungeeAuthPlugin;
|
private BungeeAuthPlugin bungeeAuthPlugin;
|
||||||
private final MojangApiConnector mojangApiConnector = new MojangApiConnector(this);
|
|
||||||
private Storage storage;
|
|
||||||
private Configuration configuration;
|
private Configuration configuration;
|
||||||
|
|
||||||
private final Random random = new Random();
|
private final Random random = new Random();
|
||||||
@ -49,6 +43,8 @@ public class FastLoginBungee extends Plugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
|
loginCore.setMojangApiConnector(new MojangApiBungee(loginCore));
|
||||||
|
|
||||||
if (!getDataFolder().exists()) {
|
if (!getDataFolder().exists()) {
|
||||||
getDataFolder().mkdir();
|
getDataFolder().mkdir();
|
||||||
}
|
}
|
||||||
@ -72,14 +68,9 @@ public class FastLoginBungee extends Plugin {
|
|||||||
|
|
||||||
String username = configuration.getString("username", "");
|
String username = configuration.getString("username", "");
|
||||||
String password = configuration.getString("password", "");
|
String password = configuration.getString("password", "");
|
||||||
storage = new Storage(this, driver, host, port, database, username, password);
|
if (!loginCore.setupDatabase(driver, host, port, database, username, password)) {
|
||||||
try {
|
|
||||||
storage.createTables();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
getLogger().log(Level.SEVERE, "Failed to setup database. Disabling plugin...", ex);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (IOException ioExc) {
|
} catch (IOException ioExc) {
|
||||||
getLogger().log(Level.SEVERE, "Error loading config. Disabling plugin...", ioExc);
|
getLogger().log(Level.SEVERE, "Error loading config. Disabling plugin...", ioExc);
|
||||||
return;
|
return;
|
||||||
@ -98,7 +89,7 @@ public class FastLoginBungee extends Plugin {
|
|||||||
public String generateStringPassword() {
|
public String generateStringPassword() {
|
||||||
StringBuilder generatedPassword = new StringBuilder(8);
|
StringBuilder generatedPassword = new StringBuilder(8);
|
||||||
for (int i = 1; i <= 8; i++) {
|
for (int i = 1; i <= 8; i++) {
|
||||||
generatedPassword.append(CHARACTERS[random.nextInt(CHARACTERS.length - 1)]);
|
generatedPassword.append(PASSWORD_CHARACTERS[random.nextInt(PASSWORD_CHARACTERS.length - 1)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return generatedPassword.toString();
|
return generatedPassword.toString();
|
||||||
@ -106,23 +97,21 @@ public class FastLoginBungee extends Plugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
if (storage != null) {
|
loginCore.close();
|
||||||
storage.close();
|
}
|
||||||
}
|
|
||||||
|
public FastLoginCore getCore() {
|
||||||
|
return loginCore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthPluginHook(BungeeAuthPlugin authPlugin) {
|
||||||
|
this.bungeeAuthPlugin = authPlugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Configuration getConfiguration() {
|
public Configuration getConfiguration() {
|
||||||
return configuration;
|
return configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Storage getStorage() {
|
|
||||||
return storage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MojangApiConnector getMojangApiConnector() {
|
|
||||||
return mojangApiConnector;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConcurrentMap<PendingConnection, Object> getPendingAutoRegister() {
|
public ConcurrentMap<PendingConnection, Object> getPendingAutoRegister() {
|
||||||
return pendingAutoRegister;
|
return pendingAutoRegister;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.github.games647.fastlogin.bungee;
|
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.bungee.hooks.BungeeAuthPlugin;
|
||||||
import com.google.common.io.ByteArrayDataOutput;
|
import com.google.common.io.ByteArrayDataOutput;
|
||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
@ -23,7 +24,7 @@ public class ForceLoginTask implements Runnable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
PlayerProfile playerProfile = plugin.getStorage().getProfile(player.getName(), false);
|
PlayerProfile playerProfile = plugin.getCore().getStorage().getProfile(player.getName(), false);
|
||||||
|
|
||||||
//force login only on success
|
//force login only on success
|
||||||
if (player.getPendingConnection().isOnlineMode()) {
|
if (player.getPendingConnection().isOnlineMode()) {
|
||||||
@ -46,7 +47,7 @@ public class ForceLoginTask implements Runnable {
|
|||||||
//cracked player
|
//cracked player
|
||||||
//update only on success to prevent corrupt data
|
//update only on success to prevent corrupt data
|
||||||
playerProfile.setPremium(false);
|
playerProfile.setPremium(false);
|
||||||
plugin.getStorage().save(playerProfile);
|
plugin.getCore().getStorage().save(playerProfile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.github.games647.fastlogin.bungee;
|
||||||
|
|
||||||
|
import com.github.games647.fastlogin.core.FastLoginCore;
|
||||||
|
import com.github.games647.fastlogin.core.MojangApiConnector;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import net.md_5.bungee.BungeeCord;
|
||||||
|
|
||||||
|
public class MojangApiBungee extends MojangApiConnector {
|
||||||
|
|
||||||
|
public MojangApiBungee(FastLoginCore plugin) {
|
||||||
|
super(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected UUID getUUIDFromJson(String json) {
|
||||||
|
MojangPlayer mojangPlayer = BungeeCord.getInstance().gson.fromJson(json, MojangPlayer.class);
|
||||||
|
return FastLoginCore.parseId(mojangPlayer.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasJoinedServer(Object session, String serverId) {
|
||||||
|
//this is not needed in Bungee
|
||||||
|
throw new UnsupportedOperationException("Not supported");
|
||||||
|
}
|
||||||
|
}
|
@ -1,211 +0,0 @@
|
|||||||
package com.github.games647.fastlogin.bungee;
|
|
||||||
|
|
||||||
import com.google.common.cache.CacheBuilder;
|
|
||||||
import com.google.common.cache.CacheLoader;
|
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
|
||||||
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.concurrent.TimeUnit;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import net.md_5.bungee.api.scheduler.GroupedThreadFactory;
|
|
||||||
|
|
||||||
public class Storage {
|
|
||||||
|
|
||||||
private static final String PREMIUM_TABLE = "premium";
|
|
||||||
|
|
||||||
private final ConcurrentMap<String, PlayerProfile> profileCache = CacheBuilder
|
|
||||||
.<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.");
|
|
||||||
}
|
|
||||||
}).asMap();
|
|
||||||
|
|
||||||
private final HikariDataSource dataSource;
|
|
||||||
private final FastLoginBungee plugin;
|
|
||||||
|
|
||||||
public Storage(FastLoginBungee plugin, String driver, String host, int port, String databasePath
|
|
||||||
, String user, String pass) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
|
|
||||||
HikariConfig databaseConfig = new HikariConfig();
|
|
||||||
databaseConfig.setUsername(user);
|
|
||||||
databaseConfig.setPassword(pass);
|
|
||||||
databaseConfig.setDriverClassName(driver);
|
|
||||||
String pluginName = plugin.getDescription().getName();
|
|
||||||
|
|
||||||
//set a custom thread factory to remove BungeeCord warning about different threads
|
|
||||||
databaseConfig.setThreadFactory(new ThreadFactoryBuilder()
|
|
||||||
.setNameFormat(pluginName + " Database Pool Thread #%1$d")
|
|
||||||
//Hikari create daemons by default
|
|
||||||
.setDaemon(true)
|
|
||||||
.setThreadFactory(new GroupedThreadFactory(plugin, pluginName)).build());
|
|
||||||
|
|
||||||
databasePath = databasePath.replace("{pluginDir}", plugin.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 = FastLoginBungee.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) {
|
|
||||||
plugin.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) {
|
|
||||||
plugin.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) {
|
|
||||||
plugin.getLogger().log(Level.SEVERE, "Failed to close connection", sqlEx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,72 @@
|
|||||||
|
package com.github.games647.fastlogin.bungee.listener;
|
||||||
|
|
||||||
|
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||||
|
import com.github.games647.fastlogin.core.PlayerProfile;
|
||||||
|
|
||||||
|
import net.md_5.bungee.api.ChatColor;
|
||||||
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
|
||||||
|
public class AsyncStatusMessage implements Runnable {
|
||||||
|
|
||||||
|
private final FastLoginBungee plugin;
|
||||||
|
private final ProxiedPlayer fromPlayer;
|
||||||
|
private final String targetPlayer;
|
||||||
|
private final boolean toPremium;
|
||||||
|
|
||||||
|
public AsyncStatusMessage(FastLoginBungee plugin, ProxiedPlayer fromPlayer, String targetPlayer, boolean toPremium) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.fromPlayer = fromPlayer;
|
||||||
|
this.targetPlayer = targetPlayer;
|
||||||
|
this.toPremium = toPremium;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (toPremium) {
|
||||||
|
activatePremium();
|
||||||
|
} else {
|
||||||
|
turnOffPremium();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void turnOffPremium() {
|
||||||
|
PlayerProfile playerProfile = plugin.getCore().getStorage().getProfile(targetPlayer, true);
|
||||||
|
if (!playerProfile.isPremium()) {
|
||||||
|
if (fromPlayer.isConnected()) {
|
||||||
|
TextComponent textComponent = new TextComponent("You are not in the premium list");
|
||||||
|
textComponent.setColor(ChatColor.DARK_RED);
|
||||||
|
fromPlayer.sendMessage(textComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
playerProfile.setPremium(false);
|
||||||
|
playerProfile.setUuid(null);
|
||||||
|
plugin.getCore().getStorage().save(playerProfile);
|
||||||
|
TextComponent textComponent = new TextComponent("Removed to the list of premium players");
|
||||||
|
textComponent.setColor(ChatColor.DARK_GREEN);
|
||||||
|
fromPlayer.sendMessage(textComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void activatePremium() {
|
||||||
|
PlayerProfile playerProfile = plugin.getCore().getStorage().getProfile(targetPlayer, true);
|
||||||
|
if (playerProfile.isPremium()) {
|
||||||
|
if (fromPlayer.isConnected()) {
|
||||||
|
TextComponent textComponent = new TextComponent("You are already on the premium list");
|
||||||
|
textComponent.setColor(ChatColor.DARK_RED);
|
||||||
|
fromPlayer.sendMessage(textComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
fromPlayer.sendMessage(textComponent);
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
package com.github.games647.fastlogin.bungee.listener;
|
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.FastLoginBungee;
|
||||||
import com.github.games647.fastlogin.bungee.ForceLoginTask;
|
import com.github.games647.fastlogin.bungee.ForceLoginTask;
|
||||||
import com.github.games647.fastlogin.bungee.PlayerProfile;
|
import com.github.games647.fastlogin.core.PlayerProfile;
|
||||||
import com.github.games647.fastlogin.bungee.hooks.BungeeAuthPlugin;
|
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
@ -36,45 +36,13 @@ public class PlayerConnectionListener implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPreLogin(final PreLoginEvent preLoginEvent) {
|
public void onPreLogin(PreLoginEvent preLoginEvent) {
|
||||||
if (preLoginEvent.isCancelled()) {
|
if (preLoginEvent.isCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
preLoginEvent.registerIntent(plugin);
|
preLoginEvent.registerIntent(plugin);
|
||||||
ProxyServer.getInstance().getScheduler().runAsync(plugin, new Runnable() {
|
ProxyServer.getInstance().getScheduler().runAsync(plugin, new AsyncPremiumCheck(plugin, preLoginEvent));
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
PendingConnection connection = preLoginEvent.getConnection();
|
|
||||||
String username = connection.getName();
|
|
||||||
try {
|
|
||||||
PlayerProfile playerProfile = plugin.getStorage().getProfile(username, true);
|
|
||||||
if (playerProfile != null) {
|
|
||||||
if (playerProfile.isPremium()) {
|
|
||||||
if (playerProfile.getUserId() != -1) {
|
|
||||||
connection.setOnlineMode(true);
|
|
||||||
}
|
|
||||||
} else if (playerProfile.getUserId() == -1) {
|
|
||||||
//user not exists in the db
|
|
||||||
BungeeAuthPlugin authPlugin = plugin.getBungeeAuthPlugin();
|
|
||||||
if (plugin.getConfiguration().getBoolean("autoRegister")
|
|
||||||
&& (authPlugin == null || !authPlugin.isRegistered(username))) {
|
|
||||||
UUID premiumUUID = plugin.getMojangApiConnector().getPremiumUUID(username);
|
|
||||||
if (premiumUUID != null) {
|
|
||||||
plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
|
|
||||||
connection.setOnlineMode(true);
|
|
||||||
plugin.getPendingAutoRegister().put(connection, new Object());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
plugin.getLogger().log(Level.SEVERE, "Failed to check premium state", ex);
|
|
||||||
} finally {
|
|
||||||
preLoginEvent.completeIntent(plugin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
@ -83,7 +51,7 @@ public class PlayerConnectionListener implements Listener {
|
|||||||
PendingConnection connection = player.getPendingConnection();
|
PendingConnection connection = player.getPendingConnection();
|
||||||
String username = connection.getName();
|
String username = connection.getName();
|
||||||
if (connection.isOnlineMode()) {
|
if (connection.isOnlineMode()) {
|
||||||
PlayerProfile playerProfile = plugin.getStorage().getProfile(player.getName(), false);
|
PlayerProfile playerProfile = plugin.getCore().getStorage().getProfile(player.getName(), false);
|
||||||
playerProfile.setUuid(player.getUniqueId());
|
playerProfile.setUuid(player.getUniqueId());
|
||||||
|
|
||||||
//bungeecord will do this automatically so override it on disabled option
|
//bungeecord will do this automatically so override it on disabled option
|
||||||
@ -92,12 +60,11 @@ public class PlayerConnectionListener implements Listener {
|
|||||||
try {
|
try {
|
||||||
UUID offlineUUID = UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(Charsets.UTF_8));
|
UUID offlineUUID = UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(Charsets.UTF_8));
|
||||||
|
|
||||||
|
//bungeecord doesn't support overriding the premium uuid
|
||||||
|
//so we have to do it with reflection
|
||||||
Field idField = initialHandler.getClass().getDeclaredField("uniqueId");
|
Field idField = initialHandler.getClass().getDeclaredField("uniqueId");
|
||||||
idField.setAccessible(true);
|
idField.setAccessible(true);
|
||||||
idField.set(connection, offlineUUID);
|
idField.set(connection, offlineUUID);
|
||||||
|
|
||||||
//bungeecord doesn't support overriding the premium uuid
|
|
||||||
// connection.setUniqueId(offlineUUID);
|
|
||||||
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||||
plugin.getLogger().log(Level.SEVERE, "Failed to set offline uuid", ex);
|
plugin.getLogger().log(Level.SEVERE, "Failed to set offline uuid", ex);
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
package com.github.games647.fastlogin.bungee.listener;
|
package com.github.games647.fastlogin.bungee.listener;
|
||||||
|
|
||||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||||
import com.github.games647.fastlogin.bungee.PlayerProfile;
|
import com.github.games647.fastlogin.core.PlayerProfile;
|
||||||
import com.google.common.io.ByteArrayDataInput;
|
import com.google.common.io.ByteArrayDataInput;
|
||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
|
|
||||||
import net.md_5.bungee.api.ChatColor;
|
|
||||||
import net.md_5.bungee.api.ProxyServer;
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
import net.md_5.bungee.api.chat.TextComponent;
|
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
import net.md_5.bungee.api.connection.Server;
|
import net.md_5.bungee.api.connection.Server;
|
||||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
import net.md_5.bungee.api.event.PluginMessageEvent;
|
||||||
@ -44,66 +42,25 @@ public class PluginMessageListener implements Listener {
|
|||||||
ByteArrayDataInput dataInput = ByteStreams.newDataInput(data);
|
ByteArrayDataInput dataInput = ByteStreams.newDataInput(data);
|
||||||
String subchannel = dataInput.readUTF();
|
String subchannel = dataInput.readUTF();
|
||||||
|
|
||||||
final ProxiedPlayer forPlayer = (ProxiedPlayer) pluginMessageEvent.getReceiver();
|
ProxiedPlayer fromPlayer = (ProxiedPlayer) pluginMessageEvent.getReceiver();
|
||||||
if ("ON".equals(subchannel)) {
|
if ("ON".equals(subchannel)) {
|
||||||
final String playerName = dataInput.readUTF();
|
String playerName = dataInput.readUTF();
|
||||||
|
|
||||||
ProxyServer.getInstance().getScheduler().runAsync(plugin, new Runnable() {
|
AsyncStatusMessage task = new AsyncStatusMessage(plugin, fromPlayer, playerName, true);
|
||||||
@Override
|
ProxyServer.getInstance().getScheduler().runAsync(plugin, task);
|
||||||
public void run() {
|
|
||||||
PlayerProfile playerProfile = plugin.getStorage().getProfile(playerName, true);
|
|
||||||
if (playerProfile.isPremium()) {
|
|
||||||
if (forPlayer.isConnected()) {
|
|
||||||
TextComponent textComponent = new TextComponent("You are already on the premium list");
|
|
||||||
textComponent.setColor(ChatColor.DARK_RED);
|
|
||||||
forPlayer.sendMessage(textComponent);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
playerProfile.setPremium(true);
|
|
||||||
//todo: set uuid
|
|
||||||
plugin.getStorage().save(playerProfile);
|
|
||||||
TextComponent textComponent = new TextComponent("Added to the list of premium players");
|
|
||||||
textComponent.setColor(ChatColor.DARK_GREEN);
|
|
||||||
forPlayer.sendMessage(textComponent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if ("OFF".equals(subchannel)) {
|
} else if ("OFF".equals(subchannel)) {
|
||||||
final String playerName = dataInput.readUTF();
|
String playerName = dataInput.readUTF();
|
||||||
|
|
||||||
ProxyServer.getInstance().getScheduler().runAsync(plugin, new Runnable() {
|
AsyncStatusMessage task = new AsyncStatusMessage(plugin, fromPlayer, playerName, false);
|
||||||
@Override
|
ProxyServer.getInstance().getScheduler().runAsync(plugin, task);
|
||||||
public void run() {
|
|
||||||
PlayerProfile playerProfile = plugin.getStorage().getProfile(playerName, true);
|
|
||||||
if (!playerProfile.isPremium()) {
|
|
||||||
if (forPlayer.isConnected()) {
|
|
||||||
TextComponent textComponent = new TextComponent("You are not in the premium list");
|
|
||||||
textComponent.setColor(ChatColor.DARK_RED);
|
|
||||||
forPlayer.sendMessage(textComponent);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
playerProfile.setPremium(false);
|
|
||||||
playerProfile.setUuid(null);
|
|
||||||
plugin.getStorage().save(playerProfile);
|
|
||||||
TextComponent textComponent = new TextComponent("Removed to the list of premium players");
|
|
||||||
textComponent.setColor(ChatColor.DARK_GREEN);
|
|
||||||
forPlayer.sendMessage(textComponent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if ("SUCCESS".equals(subchannel)) {
|
} else if ("SUCCESS".equals(subchannel)) {
|
||||||
if (forPlayer.getPendingConnection().isOnlineMode()) {
|
if (fromPlayer.getPendingConnection().isOnlineMode()) {
|
||||||
//bukkit module successfully received and force logged in the user
|
//bukkit module successfully received and force logged in the user
|
||||||
//update only on success to prevent corrupt data
|
//update only on success to prevent corrupt data
|
||||||
PlayerProfile playerProfile = plugin.getStorage().getProfile(forPlayer.getName(), false);
|
PlayerProfile playerProfile = plugin.getCore().getStorage().getProfile(fromPlayer.getName(), false);
|
||||||
playerProfile.setPremium(true);
|
playerProfile.setPremium(true);
|
||||||
//we override this in the loginevent
|
//we override this in the loginevent
|
||||||
// playerProfile.setUuid(forPlayer.getUniqueId());
|
plugin.getCore().getStorage().save(playerProfile);
|
||||||
plugin.getStorage().save(playerProfile);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
32
core/pom.xml
Normal file
32
core/pom.xml
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,4 @@
|
|||||||
package com.github.games647.fastlogin.bungee;
|
package com.github.games647.fastlogin.core;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -11,17 +9,12 @@ import java.util.UUID;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import net.md_5.bungee.BungeeCord;
|
public abstract class MojangApiConnector {
|
||||||
|
|
||||||
public class MojangApiConnector {
|
|
||||||
|
|
||||||
//http connection, read timeout and user agent for a connection to mojang api servers
|
//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 int TIMEOUT = 1 * 1_000;
|
||||||
private static final String USER_AGENT = "Premium-Checker";
|
private static final String USER_AGENT = "Premium-Checker";
|
||||||
|
|
||||||
//mojang api check to prove a player is logged in minecraft and made a join server request
|
|
||||||
private static final String HAS_JOINED_URL = "https://sessionserver.mojang.com/session/minecraft/hasJoined?";
|
|
||||||
|
|
||||||
//only premium (paid account) users have a uuid from here
|
//only premium (paid account) users have a uuid from here
|
||||||
private static final String UUID_LINK = "https://api.mojang.com/users/profiles/minecraft/";
|
private static final String UUID_LINK = "https://api.mojang.com/users/profiles/minecraft/";
|
||||||
//this includes a-zA-Z1-9_
|
//this includes a-zA-Z1-9_
|
||||||
@ -30,11 +23,9 @@ public class MojangApiConnector {
|
|||||||
//compile the pattern only on plugin enable -> and this have to be threadsafe
|
//compile the pattern only on plugin enable -> and this have to be threadsafe
|
||||||
private final Pattern playernameMatcher = Pattern.compile(VALID_PLAYERNAME);
|
private final Pattern playernameMatcher = Pattern.compile(VALID_PLAYERNAME);
|
||||||
|
|
||||||
private final FastLoginBungee plugin;
|
protected final FastLoginCore plugin;
|
||||||
|
|
||||||
private final Gson gson = new Gson();
|
public MojangApiConnector(FastLoginCore plugin) {
|
||||||
|
|
||||||
public MojangApiConnector(FastLoginBungee plugin) {
|
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,8 +44,7 @@ public class MojangApiConnector {
|
|||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||||
String line = reader.readLine();
|
String line = reader.readLine();
|
||||||
if (line != null && !line.equals("null")) {
|
if (line != null && !line.equals("null")) {
|
||||||
MojangPlayer mojangPlayer = BungeeCord.getInstance().gson.fromJson(line, MojangPlayer.class);
|
return getUUIDFromJson(line);
|
||||||
return FastLoginBungee.parseId(mojangPlayer.getId());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//204 - no content for not found
|
//204 - no content for not found
|
||||||
@ -67,10 +57,14 @@ public class MojangApiConnector {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpURLConnection getConnection(String url) throws IOException {
|
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();
|
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
|
||||||
connection.setConnectTimeout(TIMEOUT);
|
connection.setConnectTimeout(TIMEOUT);
|
||||||
connection.setReadTimeout(TIMEOUT);
|
connection.setReadTimeout(2 * TIMEOUT);
|
||||||
//the new Mojang API just uses json as response
|
//the new Mojang API just uses json as response
|
||||||
connection.setRequestProperty("Content-Type", "application/json");
|
connection.setRequestProperty("Content-Type", "application/json");
|
||||||
connection.setRequestProperty("User-Agent", USER_AGENT);
|
connection.setRequestProperty("User-Agent", USER_AGENT);
|
@ -1,4 +1,4 @@
|
|||||||
package com.github.games647.fastlogin.bungee;
|
package com.github.games647.fastlogin.core;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ public class PlayerProfile {
|
|||||||
return userId;
|
return userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected synchronized void setUserId(long generatedId) {
|
public synchronized void setUserId(long generatedId) {
|
||||||
this.userId = generatedId;
|
this.userId = generatedId;
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,5 @@
|
|||||||
package com.github.games647.fastlogin.bukkit;
|
package com.github.games647.fastlogin.core;
|
||||||
|
|
||||||
import com.comphenix.protocol.utility.SafeCacheBuilder;
|
|
||||||
import com.google.common.cache.CacheLoader;
|
|
||||||
import com.zaxxer.hikari.HikariConfig;
|
import com.zaxxer.hikari.HikariConfig;
|
||||||
import com.zaxxer.hikari.HikariDataSource;
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
|
|
||||||
@ -12,38 +10,28 @@ import java.sql.SQLException;
|
|||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
public class Storage {
|
public class Storage {
|
||||||
|
|
||||||
private static final String PREMIUM_TABLE = "premium";
|
private static final String PREMIUM_TABLE = "premium";
|
||||||
|
|
||||||
private final ConcurrentMap<String, PlayerProfile> profileCache = SafeCacheBuilder
|
private final FastLoginCore core;
|
||||||
.<String, PlayerProfile>newBuilder()
|
private final ConcurrentMap<String, PlayerProfile> profileCache;
|
||||||
.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.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
private final HikariDataSource dataSource;
|
private final HikariDataSource dataSource;
|
||||||
private final FastLoginBukkit plugin;
|
|
||||||
|
|
||||||
public Storage(FastLoginBukkit plugin, String driver, String host, int port, String databasePath
|
public Storage(FastLoginCore core, String driver, String host, int port, String databasePath
|
||||||
, String user, String pass) {
|
, String user, String pass) {
|
||||||
this.plugin = plugin;
|
this.core = core;
|
||||||
|
this.profileCache = core.buildCache();
|
||||||
|
|
||||||
HikariConfig databaseConfig = new HikariConfig();
|
HikariConfig databaseConfig = new HikariConfig();
|
||||||
databaseConfig.setUsername(user);
|
databaseConfig.setUsername(user);
|
||||||
databaseConfig.setPassword(pass);
|
databaseConfig.setPassword(pass);
|
||||||
databaseConfig.setDriverClassName(driver);
|
databaseConfig.setDriverClassName(driver);
|
||||||
|
databaseConfig.setThreadFactory(core.getThreadFactory());
|
||||||
|
|
||||||
databasePath = databasePath.replace("{pluginDir}", plugin.getDataFolder().getAbsolutePath());
|
databasePath = databasePath.replace("{pluginDir}", core.getDataFolder().getAbsolutePath());
|
||||||
|
|
||||||
String jdbcUrl = "jdbc:";
|
String jdbcUrl = "jdbc:";
|
||||||
if (driver.contains("sqlite")) {
|
if (driver.contains("sqlite")) {
|
||||||
@ -104,7 +92,7 @@ public class Storage {
|
|||||||
if (unparsedUUID == null) {
|
if (unparsedUUID == null) {
|
||||||
uuid = null;
|
uuid = null;
|
||||||
} else {
|
} else {
|
||||||
uuid = FastLoginBukkit.parseId(unparsedUUID);
|
uuid = FastLoginCore.parseId(unparsedUUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
// String name = resultSet.getString(3);
|
// String name = resultSet.getString(3);
|
||||||
@ -120,7 +108,7 @@ public class Storage {
|
|||||||
return crackedProfile;
|
return crackedProfile;
|
||||||
}
|
}
|
||||||
} catch (SQLException sqlEx) {
|
} catch (SQLException sqlEx) {
|
||||||
plugin.getLogger().log(Level.SEVERE, "Failed to query profile", sqlEx);
|
core.getLogger().log(Level.SEVERE, "Failed to query profile", sqlEx);
|
||||||
} finally {
|
} finally {
|
||||||
closeQuietly(con);
|
closeQuietly(con);
|
||||||
}
|
}
|
||||||
@ -176,7 +164,7 @@ public class Storage {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
plugin.getLogger().log(Level.SEVERE, "Failed to save playerProfile", ex);
|
core.getLogger().log(Level.SEVERE, "Failed to save playerProfile", ex);
|
||||||
} finally {
|
} finally {
|
||||||
closeQuietly(con);
|
closeQuietly(con);
|
||||||
}
|
}
|
||||||
@ -194,7 +182,7 @@ public class Storage {
|
|||||||
try {
|
try {
|
||||||
con.close();
|
con.close();
|
||||||
} catch (SQLException sqlEx) {
|
} catch (SQLException sqlEx) {
|
||||||
plugin.getLogger().log(Level.SEVERE, "Failed to close connection", sqlEx);
|
core.getLogger().log(Level.SEVERE, "Failed to close connection", sqlEx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,7 +3,7 @@
|
|||||||
# Source code: https://github.com/games647/FastLogin
|
# Source code: https://github.com/games647/FastLogin
|
||||||
#
|
#
|
||||||
# You can access the newest config here:
|
# You can access the newest config here:
|
||||||
# https://github.com/games647/FastLogin/blob/master/bukkit/src/main/resources/config.yml
|
# 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
|
# Request a premium login without forcing the player to type a command
|
||||||
#
|
#
|
18
core/src/main/resources/messages.yml
Normal file
18
core/src/main/resources/messages.yml
Normal file
@ -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 ================================
|
||||||
|
|
||||||
|
|
19
pom.xml
19
pom.xml
@ -8,7 +8,7 @@
|
|||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<name>FastLogin</name>
|
<name>FastLogin</name>
|
||||||
<version>1.3.1</version>
|
<version>1.4</version>
|
||||||
<inceptionYear>2015</inceptionYear>
|
<inceptionYear>2015</inceptionYear>
|
||||||
<url>https://www.spigotmc.org/resources/fastlogin.14153/</url>
|
<url>https://www.spigotmc.org/resources/fastlogin.14153/</url>
|
||||||
<description>
|
<description>
|
||||||
@ -22,6 +22,7 @@
|
|||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
|
<module>core</module>
|
||||||
<module>bukkit</module>
|
<module>bukkit</module>
|
||||||
<module>bungee</module>
|
<module>bungee</module>
|
||||||
<module>universal</module>
|
<module>universal</module>
|
||||||
@ -83,20 +84,4 @@
|
|||||||
</resource>
|
</resource>
|
||||||
</resources>
|
</resources>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
<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>
|
</project>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.github.games647</groupId>
|
<groupId>com.github.games647</groupId>
|
||||||
<artifactId>fastlogin</artifactId>
|
<artifactId>fastlogin</artifactId>
|
||||||
<version>1.3.1</version>
|
<version>1.4</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
@ -47,6 +47,12 @@
|
|||||||
</build>
|
</build>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>fastlogin.core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>${project.groupId}</groupId>
|
<groupId>${project.groupId}</groupId>
|
||||||
<artifactId>fastlogin.bukkit</artifactId>
|
<artifactId>fastlogin.bukkit</artifactId>
|
||||||
|
Reference in New Issue
Block a user