Compare commits

..

34 Commits
1.1 ... 1.5.1

Author SHA1 Message Date
9fc7e0bf43 Fixed BungeeCord support by correctly saving the proxy ids (Fixes #22) 2016-06-11 10:32:53 +02:00
ac8bcb1758 Fix default config deploy 2016-06-11 09:29:55 +02:00
bebcb3e9de Make locale messages thread-safe 2016-06-10 10:37:04 +02:00
0b899f61a8 Fixed message removal 2016-06-10 09:23:15 +02:00
7733135ce4 Fixed NPE in BungeeCord on cracked login for existing players (#22) 2016-06-10 08:57:08 +02:00
be89eec23b Fix NPE on premium name check if it's pure cracked player
(Fixes #21)
2016-06-10 08:53:06 +02:00
679060d4e9 Fixed support for empty messages 2016-06-09 14:33:14 +02:00
f6aa064835 Added localization messages (Fixes #20) 2016-06-09 12:43:04 +02:00
de4b73c3bd Upgrade maven plugin version 2016-06-09 10:30:42 +02:00
ac15829dcc Fixes insert (new player) for cracked players (Fixes #18) 2016-06-07 17:32:45 +02:00
0b709997a4 Fix duplicate premium uuid check in BungeeCord 2016-06-07 16:46:18 +02:00
8809875ca4 Fixed setting auth hook 2016-06-04 14:56:04 +02:00
aa30c070b9 Add database importers (planned) 2016-06-02 15:02:15 +02:00
51d0aefbf3 Added support of detecting name changes (Fixes #18) 2016-06-01 14:42:48 +02:00
cb876a52bd Add support for multiple bungeecords (Fixes #19) 2016-05-28 17:21:08 +02:00
3e844be65d Clean up project structure 2016-05-26 11:04:13 +02:00
dce95cf0d0 Prevent thread create violation in BungeeCord 2016-05-25 09:40:43 +02:00
81eeaeae83 Fix recursive call for bungeecord 2016-05-23 21:11:34 +02:00
6b1542de88 Now bungeeCord detection should work for all server versions 2016-05-23 17:02:32 +02:00
99b7367366 Fixed bungeecord support in Cauldron (Related to #11) 2016-05-23 12:02:44 +02:00
961b144efb Load the plugin before worlds loading and auth plugins (Related to #12)
to display the message not fully started more less
2016-05-23 10:27:01 +02:00
dcd06ad613 Fix server not fully started message on ProtocolSupport or Bungee
(Fixes #15)
2016-05-23 08:46:18 +02:00
c4c043e1c5 Fix AuthMe 3.X forceLogin on autoRegister (Fixes #14) 2016-05-22 20:00:07 +02:00
87aa9dd668 Fixed CrazyLogin hook 2016-05-22 18:34:21 +02:00
2838c06ab3 Replacing guava's class search with an explicit list (Fixes #11)
-> Fixed 1.7 Minecraft support 
-> Fixed Cauldron support
2016-05-22 18:31:34 +02:00
ae58e0539a Added support for LogIt 2016-05-22 13:59:41 +02:00
624745728f Added other command argument to /premium and /cracked (Fixes #13) 2016-05-21 13:32:48 +02:00
d0287ec2b4 Fixed premium logins if the server is not fully started (Fixes #12) 2016-05-18 18:41:24 +02:00
e6a4af92cc Add support for AuthMe 3.X 2016-05-18 15:47:51 +02:00
8f3920fa99 Fix message order 2016-05-15 17:33:21 +02:00
a723b2ddd3 Added BungeeCord setup description 2016-05-14 14:00:43 +02:00
5cf67127c7 Fix dead lock in xAuth 2016-05-14 13:30:32 +02:00
e5309b9fa1 Remove the check for auth plugins in order to allow auth plugins to
register their hook after the initialization of FastLogin
2016-05-14 13:25:12 +02:00
e439126294 Added API methods for auth plugins to set their own hook 2016-05-14 12:27:03 +02:00
56 changed files with 1928 additions and 1255 deletions

3
.gitignore vendored
View File

@ -45,4 +45,5 @@ gradle-app.setting
# Project module targets
bukkit/target
universal/target
bungee/target
bungee/target
core/target

View File

@ -1,3 +1,45 @@
######1.5.1
* Fixed BungeeCord support by correctly saving the proxy ids
######1.5
* Added localization
* Fixed NPE on premium name check if it's pure cracked player
* Fixed NPE in BungeeCord on cracked login for existing players
* Fixed saving of existing cracked players
######1.4
* Added Bungee setAuthPlugin method
* Added nameChangeCheck
* Multiple BungeeCord support
######1.3.1
* Prevent thread create violation in BungeeCord
######1.3
* Added support for AuthMe 3.X
* Fixed premium logins if the server is not fully started
* Added other command argument to /premium and /cracked
* Added support for LogIt
* Fixed 1.7 Minecraft support by removing guava 11+ only features -> Cauldron support
* Fixed BungeeCord support in Cauldron
######1.2.1
* Fix premium status change notification message on BungeeCord
######1.2
* Fix race condition in BungeeCord
* Fix dead lock in xAuth
* Added API methods for plugins to set their own password generator
* Added API methods for plugins to set their own auth plugin hook
=> Added support for AdvancedLogin
######1.1
* Make the configuration options also work under BungeeCord (premiumUUID, forwardSkin)
@ -102,4 +144,4 @@
* Added better error handling
#####0.1
* First release
* First release

View File

@ -3,7 +3,7 @@
[![Build Status](https://travis-ci.org/games647/FastLogin.svg?branch=master)](https://travis-ci.org/games647/FastLogin)
[![Donate Button](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=8ZBULMAPN7MZC)
Checks if a minecraft player has a paid account (premium). If so, they can skip offline authentication (auth plugins).
Checks if a Minecraft player has a paid account (premium). If so, they can skip offline authentication (auth plugins).
So they don't need to enter passwords. This is also called auto login (auto-login).
###Features:
@ -15,34 +15,39 @@ So they don't need to enter passwords. This is also called auto login (auto-logi
* Forge/Sponge message support
* Premium UUID support
* Forwards Skins
* Detect user name changed and will update the existing database record
* BungeeCord support
* Auto register new premium players
* Plugin: ProtocolSupport is supported and can be used as an alternative to ProtocolLib
* No client modifications needed
* Good performance by using async non blocking operations
* Locale messages
* Free
* Open source
***
###Commands:
* /premium [player] Label the invoker as paid account
* /cracked [player] Label the invoker as cracked account
* /premium [player] Label the invoker or the argument as paid account
* /cracked [player] Label the invoker or the argument as cracked account
###Permissions:
* fastlogin.bukkit.command.premium
* fastlogin.bukkit.command.cracked
###Requirements:
* Plugin: [ProtocolLib](http://www.spigotmc.org/resources/protocollib.1997/)
* Plugin: [ProtocolLib](http://www.spigotmc.org/resources/protocollib.1997/) or [ProtocolSupport](http://www.spigotmc.org/resources/protocolsupport.7201/)
* Tested Bukkit/[Spigot](https://www.spigotmc.org) 1.9 (could also work with other versions)
* Java 7+
* Run Spigot and/or BungeeCord/Waterfall in offline mode (see server.properties or config.yml)
* An auth plugin. Supported Plugins
* An auth plugin. Supported plugins
####Bukkit/Spigot/PaperSPigot
####Bukkit/Spigot/PaperSpigot
* [AuthMe](http://dev.bukkit.org/bukkit-plugins/authme-reloaded/)
* [AuthMe (both 5.X and 3.X)](http://dev.bukkit.org/bukkit-plugins/authme-reloaded/)
* [xAuth](http://dev.bukkit.org/bukkit-plugins/xauth/)
* [LogIt](https://github.com/XziomekX/LogIt)
* [AdvancedLogin (Paid)](https://www.spigotmc.org/resources/advancedlogin.10510/)
* [CrazyLogin](http://dev.bukkit.org/bukkit-plugins/crazylogin/)
* [LoginSecurity](http://dev.bukkit.org/bukkit-plugins/loginsecurity/)
* [RoyalAuth](http://dev.bukkit.org/bukkit-plugins/royalauth/)
@ -58,10 +63,32 @@ https://www.spigotmc.org/resources/fastlogin.14153/history
***
###How to install
####Bukkit/Spigot/PaperSpigot
1. Download and install ProtocolLib
2. Download and install FastLogin
3. Set your server in offline mode by setting the value onlinemode in your server.properties to false
####BungeeCord/Waterfall
1. Activate BungeeCord in the Spigot configuration
2. Restart your server
3. Now there is proxy-whitelist file in the FastLogin folder
Put your stats id from the BungeeCord config into this file
4. Activate ipForward in your BungeeCord config
5. Download and Install FastLogin on BungeeCord AND Spigot
6. Check your database settings in the config of FastLogin on BungeeCord
7. Set your proxy (BungeeCord) in offline mode by setting the value onlinemode in your config.yml to false
8. (BungeeCord doesn't support SQLite per default, so you should change the configuration to MySQL or MariaDB)
***
###FAQ
####Index
1. [How does minecraft logins work?](#how-does-minecraft-logins-work)
1. [How does Minecraft logins work?](#how-does-minecraft-logins-work)
2. [How does this plugin work?](#how-does-this-plugin-work)
3. [Why does the plugin require offline mode?](#why-does-the-plugin-require-offline-mode)
4. [Can cracked player join with premium usernames?](#can-cracked-player-join-with-premium-usernames)
@ -137,11 +164,7 @@ and Mojang account. Then the command can be executed. So someone different canno
by buying the username.
####Does the plugin have BungeeCord support?
Yes it has. Just activate ipForward in your BungeeCord config and place the plugin in the plugins folder of
Bukkit/Spigot and BungeeCord. Then you have fill your BungeeCord Id (from the Stats-Option in the BungeeCord config)
into the whitelist file of your Bukkit/Spigot server. For security reasons, don't post this Id on Forums.
This plugin will automatically detect if BungeeCord is running and handle premium checks on BungeeCord.
Yes it has. See the how to install above.
####Could premium players have a premium UUID and Skin?
Since 0.7 both features are implemented. You can check the config.yml in order to activate it.

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin</artifactId>
<version>1.1</version>
<version>1.5.1</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -40,6 +40,11 @@
<url>http://repo.luricos.de/bukkit-plugins/</url>
</repository>
<repository>
<id>logit-only-repo</id>
<url>http://ci.ac3-servers.eu/job/LogIt-Classic/2/maven-repository/repository/</url>
</repository>
<!--Github automatic maven builds-->
<repository>
<id>jitpack.io</id>
@ -48,6 +53,13 @@
</repositories>
<dependencies>
<dependency>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin.core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<!--Server API-->
<dependency>
<groupId>org.spigotmc</groupId>
@ -60,7 +72,7 @@
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>3.6.5-SNAPSHOT</version>
<version>4.0.1</version>
<optional>true</optional>
</dependency>
@ -89,6 +101,7 @@
<dependency>
<groupId>com.github.lenis0012</groupId>
<artifactId>LoginSecurity-2</artifactId>
<!--Old version 2.0-->
<version>-9c09e73b7f-1</version>
<exclusions>
<exclusion>
@ -98,6 +111,18 @@
</exclusions>
</dependency>
<dependency>
<groupId>io.github.lucaseasedup.logit</groupId>
<artifactId>LogIt</artifactId>
<version>SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.RoyalDev</groupId>
<artifactId>RoyalAuth</artifactId>

View File

@ -0,0 +1,59 @@
package com.github.games647.fastlogin.bukkit;
import com.github.games647.fastlogin.core.FastLoginCore;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.io.File;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Logger;
import org.bukkit.ChatColor;
import org.bukkit.configuration.file.YamlConfiguration;
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 ThreadFactory getThreadFactory() {
String pluginName = plugin.getName();
return new ThreadFactoryBuilder()
.setNameFormat(pluginName + " Database Pool Thread #%1$d")
//Hikari create daemons by default
.setDaemon(true)
.build();
}
@Override
public void loadMessages() {
plugin.saveResource("messages.yml", false);
File messageFile = new File(plugin.getDataFolder(), "messages.yml");
YamlConfiguration messageConfig = YamlConfiguration.loadConfiguration(messageFile);
for (String key : messageConfig.getKeys(false)) {
String message = ChatColor.translateAlternateColorCodes('&', messageConfig.getString(key));
if (!message.isEmpty()) {
localeMessages.put(key, message);
}
}
}
@Override
public void loadConfig() {
plugin.saveDefaultConfig();
}
}

View File

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

View File

@ -0,0 +1,12 @@
package com.github.games647.fastlogin.bukkit;
import org.apache.commons.lang.RandomStringUtils;
import org.bukkit.entity.Player;
public class DefaultPasswordGenerator implements PasswordGenerator {
@Override
public String getRandomPassword(Player player) {
return RandomStringUtils.random(8, true, true);
}
}

View File

@ -1,33 +1,27 @@
package com.github.games647.fastlogin.bukkit;
import com.github.games647.fastlogin.bukkit.tasks.DelayedAuthHook;
import com.avaje.ebeaninternal.api.ClassUtil;
import com.comphenix.protocol.AsynchronousManager;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.utility.SafeCacheBuilder;
import com.github.games647.fastlogin.bukkit.commands.CrackedCommand;
import com.github.games647.fastlogin.bukkit.commands.PremiumCommand;
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
import com.github.games647.fastlogin.bukkit.listener.BukkitJoinListener;
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.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.reflect.ClassPath;
import java.io.IOException;
import java.lang.reflect.Method;
import java.security.KeyPair;
import java.sql.SQLException;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.apache.commons.lang.RandomStringUtils;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
@ -38,41 +32,46 @@ public class FastLoginBukkit extends JavaPlugin {
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
private final KeyPair keyPair = EncryptionUtil.generateKeyPair();
private boolean bungeeCord;
private Storage storage;
private final FastLoginCore core = new BukkitCore(this);
private boolean serverStarted;
//this map is thread-safe for async access (Packet Listener)
//SafeCacheBuilder is used in order to be version independent
private final ConcurrentMap<String, PlayerSession> session = SafeCacheBuilder.<String, PlayerSession>newBuilder()
private final ConcurrentMap<String, BukkitLoginSession> session = SafeCacheBuilder.<String, BukkitLoginSession>newBuilder()
//2 minutes should be enough as a timeout for bad internet connection (Server, Client and Mojang)
.expireAfterWrite(1, TimeUnit.MINUTES)
//mapped by ip:port -> PlayerSession
.build(new CacheLoader<String, PlayerSession>() {
.build(new CacheLoader<String, BukkitLoginSession>() {
@Override
public PlayerSession load(String key) throws Exception {
public BukkitLoginSession load(String key) throws Exception {
//A key should be inserted manually on start packet
throw new UnsupportedOperationException("Not supported");
}
});
private BukkitAuthPlugin authPlugin;
private final MojangApiConnector mojangApiConnector = new MojangApiConnector(this);
private PasswordGenerator passwordGenerator = new DefaultPasswordGenerator();
@Override
public void onEnable() {
saveDefaultConfig();
core.setMojangApiConnector(new MojangApiBukkit(core));
core.loadConfig();
core.loadMessages();
try {
if (ClassUtil.isPresent("org.spigotmc.SpigotConfig")) {
bungeeCord = Class.forName("org.spigotmc.SpigotConfig").getDeclaredField("bungee").getBoolean(null);
}
} catch (Exception | NoSuchMethodError ex) {
getLogger().warning("Cannot check bungeecord support. You use a non-spigot build");
ex.printStackTrace();
}
if (getServer().getOnlineMode()) {
//we need to require offline to prevent a session request for a offline player
@ -81,31 +80,9 @@ public class FastLoginBukkit extends JavaPlugin {
return;
}
try {
if (Bukkit.spigot().getConfig().isBoolean("settings.bungeecord")) {
bungeeCord = Bukkit.spigot().getConfig().getBoolean("settings.bungeecord");
} else {
Method getConfigMethod = FuzzyReflection.fromObject(getServer().spigot(), true)
.getMethodByName("getSpigotConfig");
getConfigMethod.setAccessible(true);
YamlConfiguration spigotConfig = (YamlConfiguration) getConfigMethod.invoke(getServer().spigot());
bungeeCord = spigotConfig.getBoolean("settings.bungeecord");
}
} catch (Exception | NoSuchMethodError ex) {
getLogger().warning("Cannot check bungeecord support. You use a non-spigot build");
}
boolean hookFound = registerHooks();
if (bungeeCord) {
getLogger().info("BungeeCord setting detected. No auth plugin is required");
} else if (!hookFound) {
getLogger().info("No auth plugin were found and bungeecord is deactivated. "
+ "Either one or both of the checks have to pass in order to use this plugin");
setEnabled(false);
return;
}
if (bungeeCord) {
setServerStarted();
//check for incoming messages from the bungeecord version of this plugin
getServer().getMessenger().registerIncomingPluginChannel(this, getName(), new BungeeCordListener(this));
getServer().getMessenger().registerOutgoingPluginChannel(this, getName());
@ -119,11 +96,7 @@ public class FastLoginBukkit extends JavaPlugin {
String username = getConfig().getString("username", "");
String password = getConfig().getString("password", "");
this.storage = new Storage(this, driver, host, port, database, username, password);
try {
storage.createTables();
} catch (SQLException sqlEx) {
getLogger().log(Level.SEVERE, "Failed to create database tables. Disabling plugin...", sqlEx);
if (!core.setupDatabase(driver, host, port, database, username, password)) {
setEnabled(false);
return;
}
@ -144,6 +117,9 @@ public class FastLoginBukkit extends JavaPlugin {
}
}
//delay dependency setup because we load the plugin very early where plugins are initialized yet
getServer().getScheduler().runTask(this, new DelayedAuthHook(this));
getServer().getPluginManager().registerEvents(new BukkitJoinListener(this), this);
//register commands using a unique name
@ -161,13 +137,21 @@ public class FastLoginBukkit extends JavaPlugin {
player.removeMetadata(getName(), this);
}
if (storage != null) {
storage.close();
if (core != null) {
core.close();
}
}
public String generateStringPassword() {
return RandomStringUtils.random(8, true, true);
public FastLoginCore getCore() {
return core;
}
public String generateStringPassword(Player player) {
return passwordGenerator.getRandomPassword(player);
}
public void setPasswordGenerator(PasswordGenerator passwordGenerator) {
this.passwordGenerator = passwordGenerator;
}
/**
@ -176,7 +160,7 @@ public class FastLoginBukkit extends JavaPlugin {
*
* @return a thread-safe session map
*/
public ConcurrentMap<String, PlayerSession> getSessions() {
public ConcurrentMap<String, BukkitLoginSession> getSessions() {
return session;
}
@ -189,10 +173,6 @@ public class FastLoginBukkit extends JavaPlugin {
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
* was found.
@ -200,51 +180,38 @@ public class FastLoginBukkit extends JavaPlugin {
* @return interface to any supported auth plugin
*/
public BukkitAuthPlugin getAuthPlugin() {
if (authPlugin == null) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
getLogger().log(Level.SEVERE, null, ex);
}
}
return 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 {
String hooksPackage = this.getClass().getPackage().getName() + ".hooks";
//Look through all classes in the hooks package and look for supporting plugins on the server
for (ClassPath.ClassInfo clazzInfo : ClassPath.from(getClassLoader()).getTopLevelClasses(hooksPackage)) {
//remove the hook suffix
String pluginName = clazzInfo.getSimpleName().replace("Hook", "");
Class<?> clazz = clazzInfo.load();
//uses only member classes which uses AuthPlugin interface (skip interfaces)
if (BukkitAuthPlugin.class.isAssignableFrom(clazz)
//check only for enabled plugins. A single plugin could be disabled by plugin managers
&& getServer().getPluginManager().isPluginEnabled(pluginName)) {
authPluginHook = (BukkitAuthPlugin) clazz.newInstance();
getLogger().log(Level.INFO, "Hooking into auth plugin: {0}", pluginName);
break;
}
}
} catch (InstantiationException | IllegalAccessException | IOException 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 void setAuthPluginHook(BukkitAuthPlugin authPlugin) {
this.authPlugin = authPlugin;
}
public boolean isBungeeCord() {
return bungeeCord;
}
/**
* Wait before the server is fully started. This is workaround, because connections right on startup are not
* injected by ProtocolLib
*
* @return
*/
public boolean isServerFullyStarted() {
return serverStarted;
}
public void setServerStarted() {
if (!this.serverStarted) {
this.serverStarted = true;
}
}
}

View File

@ -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 BukkitLoginSession)) {
return false;
}
BukkitLoginSession playerSession = (BukkitLoginSession) 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);
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,8 @@
package com.github.games647.fastlogin.bukkit;
import org.bukkit.entity.Player;
public interface PasswordGenerator {
String getRandomPassword(Player player);
}

View File

@ -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;
}
}

View File

@ -1,201 +0,0 @@
package com.github.games647.fastlogin.bukkit;
import com.comphenix.protocol.utility.SafeCacheBuilder;
import com.google.common.cache.CacheLoader;
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;
public class Storage {
private static final String PREMIUM_TABLE = "premium";
private final ConcurrentMap<String, PlayerProfile> profileCache = 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.");
}
});
private final HikariDataSource dataSource;
private final FastLoginBukkit plugin;
public Storage(FastLoginBukkit 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);
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 = FastLoginBukkit.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);
}
}
}
}

View File

@ -1,7 +1,7 @@
package com.github.games647.fastlogin.bukkit.commands;
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.ByteStreams;
@ -25,19 +25,19 @@ public class CrackedCommand implements CommandExecutor {
if (args.length == 0) {
if (!(sender instanceof Player)) {
//console or command block
sender.sendMessage(ChatColor.DARK_RED + "Only players can remove themselves from the premium list");
sender.sendMessage(plugin.getCore().getMessage("no-console"));
return true;
}
final Player player = (Player) sender;
// UUID uuid = player.getUniqueId();
if (plugin.isBungeeCord()) {
notifiyBungeeCord((Player) sender);
sender.sendMessage(ChatColor.YELLOW + "Sending request...");
notifiyBungeeCord(sender, sender.getName());
String message = plugin.getCore().getMessage("wait-on-proxy");
if (message != null) {
sender.sendMessage(message);
}
} else {
//todo: load async if it's not in the cache anymore
final PlayerProfile profile = plugin.getStorage().getProfile(player.getName(), true);
final PlayerProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName());
if (profile.isPremium()) {
sender.sendMessage(ChatColor.DARK_GREEN + "Removed from the list of premium players");
profile.setPremium(false);
@ -45,7 +45,7 @@ public class CrackedCommand implements CommandExecutor {
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
@Override
public void run() {
plugin.getStorage().save(profile);
plugin.getCore().getStorage().save(profile);
}
});
} else {
@ -55,27 +55,61 @@ public class CrackedCommand implements CommandExecutor {
return true;
} else {
sender.sendMessage(ChatColor.DARK_RED + "NOT IMPLEMENTED YET");
//todo:
// String playerName = args[0];
// boolean existed = plugin.getEnabledPremium().remove(playerName);
// if (existed) {
// sender.sendMessage(ChatColor.DARK_GREEN + "Removed from the list of premium players");
// notifiyBungeeCord((Player) sender);
// } else {
// sender.sendMessage(ChatColor.DARK_RED + "User is not in the premium list");
// }
if (!sender.hasPermission(command.getPermission() + ".other")) {
sender.sendMessage(plugin.getCore().getMessage("no-permission"));
return true;
}
if (plugin.isBungeeCord()) {
notifiyBungeeCord(sender, args[0]);
String message = plugin.getCore().getMessage("wait-on-proxy");
if (message != null) {
sender.sendMessage(message);
}
} else {
//todo: load async if it's not in the cache anymore
final PlayerProfile profile = plugin.getCore().getStorage().loadProfile(args[0]);
if (profile == null) {
sender.sendMessage(plugin.getCore().getMessage("player-unknown"));
return true;
}
if (profile.isPremium()) {
sender.sendMessage(ChatColor.DARK_GREEN + "Removed from the list of premium players");
profile.setPremium(false);
profile.setUuid(null);
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
@Override
public void run() {
plugin.getCore().getStorage().save(profile);
}
});
} else {
sender.sendMessage(ChatColor.DARK_RED + "Player is not in the premium list");
}
}
}
return true;
}
private void notifiyBungeeCord(Player target) {
private void notifiyBungeeCord(CommandSender sender, String target) {
if (sender instanceof Player) {
notifiyBungeeCord((Player) sender, target);
} else {
//todo: add console support
// Player firstPlayer = Iterables.getFirst(Bukkit.getOnlinePlayers(), null);
// notifiyBungeeCord(firstPlayer, target);
}
}
private void notifiyBungeeCord(Player sender, String target) {
if (plugin.isBungeeCord()) {
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
dataOutput.writeUTF("OFF");
dataOutput.writeUTF(target);
target.sendPluginMessage(plugin, plugin.getName(), dataOutput.toByteArray());
plugin.getLogger().info("No player online to send a plugin message to the proxy");
}
}
}

View File

@ -1,7 +1,7 @@
package com.github.games647.fastlogin.bukkit.commands;
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.ByteStreams;
@ -30,19 +30,19 @@ public class PremiumCommand implements CommandExecutor {
if (args.length == 0) {
if (!(sender instanceof Player)) {
//console or command block
sender.sendMessage(ChatColor.DARK_RED + "Only players can add themselves as premium");
sender.sendMessage(plugin.getCore().getMessage("no-console"));
return true;
}
Player player = (Player) sender;
// UUID uuid = player.getUniqueId();
if (plugin.isBungeeCord()) {
notifiyBungeeCord(player);
sender.sendMessage(ChatColor.YELLOW + "Sending request...");
notifiyBungeeCord(sender, sender.getName());
String message = plugin.getCore().getMessage("wait-on-proxy");
if (message != null) {
sender.sendMessage(message);
}
} else {
// //todo: load async if it's not in the cache anymore
final PlayerProfile profile = plugin.getStorage().getProfile(player.getName(), true);
final PlayerProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName());
if (profile.isPremium()) {
sender.sendMessage(ChatColor.DARK_RED + "You are already on the premium list");
} else {
@ -51,7 +51,7 @@ public class PremiumCommand implements CommandExecutor {
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
@Override
public void run() {
plugin.getStorage().save(profile);
plugin.getCore().getStorage().save(profile);
}
});
@ -61,27 +61,62 @@ public class PremiumCommand implements CommandExecutor {
return true;
} else {
sender.sendMessage(ChatColor.DARK_RED + "NOT IMPLEMENTED YET");
//todo: async load
// String playerName = args[0];
// boolean didntexist = plugin.getEnabledPremium().add(playerName);
// if (!didntexist) {
// sender.sendMessage(ChatColor.DARK_RED + "You are already on the premium list");
// } else {
// sender.sendMessage(ChatColor.DARK_GREEN + "Added to the list of premium players");
// }
// notifiyBungeeCord();
if (!sender.hasPermission(command.getPermission() + ".other")) {
sender.sendMessage(plugin.getCore().getMessage("no-permission"));
return true;
}
if (plugin.isBungeeCord()) {
notifiyBungeeCord(sender, args[0]);
String message = plugin.getCore().getMessage("wait-on-proxy");
if (message != null) {
sender.sendMessage(message);
}
} else {
//todo: load async if it's not in the cache anymore
final PlayerProfile profile = plugin.getCore().getStorage().loadProfile(args[0]);
if (profile == null) {
sender.sendMessage(plugin.getCore().getMessage("player-unknown"));
return true;
}
if (profile.isPremium()) {
sender.sendMessage(ChatColor.DARK_RED + "Player is already on the premium list");
} else {
//todo: resolve uuid
profile.setPremium(true);
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
@Override
public void run() {
plugin.getCore().getStorage().save(profile);
}
});
sender.sendMessage(ChatColor.DARK_GREEN + "Added to the list of premium players");
}
}
}
return true;
}
private void notifiyBungeeCord(Player target) {
private void notifiyBungeeCord(CommandSender sender, String target) {
if (sender instanceof Player) {
notifiyBungeeCord((Player) sender, target);
} else {
//todo: add console support
// Player firstPlayer = Iterables.getFirst(Bukkit.getOnlinePlayers(), null);
// notifiyBungeeCord(firstPlayer, target);
}
}
private void notifiyBungeeCord(Player sender, String target) {
if (plugin.isBungeeCord()) {
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
dataOutput.writeUTF("ON");
dataOutput.writeUTF(target);
target.sendPluginMessage(plugin, plugin.getName(), dataOutput.toByteArray());
sender.sendPluginMessage(plugin, plugin.getName(), dataOutput.toByteArray());
}
}
}

View File

@ -1,5 +1,8 @@
package com.github.games647.fastlogin.bukkit.hooks;
import com.avaje.ebeaninternal.api.ClassUtil;
import fr.xephi.authme.api.API;
import fr.xephi.authme.api.NewAPI;
import org.bukkit.entity.Player;
@ -13,23 +16,42 @@ import org.bukkit.entity.Player;
*/
public class AuthMeHook implements BukkitAuthPlugin {
private final boolean isNewAPIAvailable;
public AuthMeHook() {
this.isNewAPIAvailable = ClassUtil.isPresent("fr.xephi.authme.api.NewAPI");
}
@Override
public boolean forceLogin(Player player) {
//skips registration and login
NewAPI.getInstance().forceLogin(player);
//commented because the operation above is performed async -> race conditions
// return NewAPI.getInstance().isAuthenticated(player);
if (isNewAPIAvailable) {
NewAPI.getInstance().forceLogin(player);
} else {
API.forceLogin(player);
}
return true;
}
@Override
public boolean isRegistered(String playerName) throws Exception {
return NewAPI.getInstance().isRegistered(playerName);
if (isNewAPIAvailable) {
return NewAPI.getInstance().isRegistered(playerName);
} else {
return API.isRegistered(playerName);
}
}
@Override
public boolean forceRegister(Player player, String password) {
NewAPI.getInstance().forceRegister(player, password);
if (isNewAPIAvailable) {
NewAPI.getInstance().forceRegister(player, password);
} else {
API.registerPlayer(player.getName(), password);
forceLogin(player);
}
return true;
}
}

View File

@ -7,10 +7,10 @@ import de.st_ddt.crazylogin.data.LoginPlayerData;
import de.st_ddt.crazylogin.databases.CrazyLoginDataDatabase;
import de.st_ddt.crazylogin.listener.PlayerListener;
import de.st_ddt.crazylogin.metadata.Authenticated;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
import org.bukkit.Bukkit;

View File

@ -7,6 +7,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.bukkit.Achievement;
import org.bukkit.Effect;
import org.bukkit.EntityEffect;

View File

@ -0,0 +1,36 @@
package com.github.games647.fastlogin.bukkit.hooks;
import io.github.lucaseasedup.logit.CancelledState;
import io.github.lucaseasedup.logit.LogItCore;
import io.github.lucaseasedup.logit.account.Account;
import org.bukkit.entity.Player;
/**
* Github: https://github.com/XziomekX/LogIt
* Project page:
*
* Bukkit: Unknown
* Spigot: Unknown
*/
public class LogItHook implements BukkitAuthPlugin {
@Override
public boolean forceLogin(Player player) {
return LogItCore.getInstance().getSessionManager().startSession(player) == CancelledState.NOT_CANCELLED;
}
@Override
public boolean isRegistered(String playerName) throws Exception {
return LogItCore.getInstance().getAccountManager().isRegistered(playerName);
}
@Override
public boolean forceRegister(Player player, String password) {
Account account = new Account(player.getName());
account.changePassword(password);
account.setLastActiveDate(System.currentTimeMillis() / 1000);
account.setRegistrationDate(System.currentTimeMillis() / 1000);
return LogItCore.getInstance().getAccountManager().insertAccount(account) == CancelledState.NOT_CANCELLED;
}
}

View File

@ -5,14 +5,13 @@ import com.lenis0012.bukkit.ls.LoginSecurity;
import com.lenis0012.bukkit.ls.data.DataManager;
import java.net.InetAddress;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
/**
@ -27,6 +26,11 @@ import org.bukkit.entity.Player;
public class LoginSecurityHook implements BukkitAuthPlugin {
protected final LoginSecurity securityPlugin = LoginSecurity.instance;
// protected final boolean newVersion;
public LoginSecurityHook() {
// this.newVersion = ClassUtil.isPresent("com.lenis0012.bukkit.loginsecurity.session.action.LoginAction");
}
@Override
public boolean forceLogin(final Player player) {
@ -71,13 +75,13 @@ public class LoginSecurityHook implements BukkitAuthPlugin {
}
@Override
public boolean forceRegister(final Player player, final String password) {
final DataManager dataManager = securityPlugin.data;
public boolean forceRegister(Player player, String password) {
DataManager dataManager = securityPlugin.data;
UUID playerUUID = player.getUniqueId();
final String uuidString = playerUUID.toString().replace("-", "");
final InetAddress ipAddress = player.getAddress().getAddress();
final String passwordHash = securityPlugin.hasher.hash(password);
String uuidString = playerUUID.toString().replace("-", "");
InetAddress ipAddress = player.getAddress().getAddress();
String passwordHash = securityPlugin.hasher.hash(password);
//this executes a sql query without interacting with other parts so we can run it async.
dataManager.register(uuidString, passwordHash, securityPlugin.hasher.getTypeId(), ipAddress.toString());

View File

@ -6,7 +6,6 @@ import java.util.concurrent.Future;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;

View File

@ -70,10 +70,7 @@ public class xAuthHook implements BukkitAuthPlugin {
boolean registerSuccess = xAuthPlugin.getAuthClass(xAuthPlayer)
.adminRegister(player.getName(), password, null);
if (registerSuccess) {
//login in the player after registration
return forceLogin(player);
}
return registerSuccess;
}
return false;
@ -81,7 +78,13 @@ public class xAuthHook implements BukkitAuthPlugin {
});
try {
return future.get();
boolean success = future.get();
if (success) {
//login in the player after registration
return forceLogin(player);
}
return false;
} catch (InterruptedException | ExecutionException ex) {
xAuthPlugin.getLogger().log(Level.SEVERE, "Failed to forceLogin", ex);
return false;

View File

@ -2,16 +2,18 @@ package com.github.games647.fastlogin.bukkit.listener;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedSignedProperty;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.ForceLoginTask;
import com.github.games647.fastlogin.bukkit.PlayerSession;
import com.github.games647.fastlogin.bukkit.tasks.ForceLoginTask;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerLoginEvent.Result;
/**
* This listener tells authentication plugins if the player has a premium account and we checked it successfully. So the
@ -27,12 +29,18 @@ public class BukkitJoinListener implements Listener {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerLogin(PlayerLoginEvent loginEvent) {
if (loginEvent.getResult() == Result.ALLOWED && !plugin.isServerFullyStarted()) {
loginEvent.disallow(Result.KICK_OTHER, "§cServer is not fully started yet. Please retry");
}
}
@EventHandler(ignoreCancelled = true)
public void onPlayerJoin(PlayerJoinEvent joinEvent) {
final Player player = joinEvent.getPlayer();
Player player = joinEvent.getPlayer();
//removing the session because we now use it
final PlayerSession session = plugin.getSessions().get(player.getAddress().toString());
BukkitLoginSession session = plugin.getSessions().get(player.getAddress().toString());
if (session != null && plugin.getConfig().getBoolean("forwardSkin")) {
WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(player);
WrappedSignedProperty skin = session.getSkin();
@ -46,12 +54,4 @@ public class BukkitJoinListener implements Listener {
Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, new ForceLoginTask(plugin, player), DELAY_LOGIN);
}
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent quitEvent) {
final Player player = quitEvent.getPlayer();
//prevent memory leaks
player.removeMetadata(plugin.getName(), plugin);
}
}

View File

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

View File

@ -1,9 +1,9 @@
package com.github.games647.fastlogin.bukkit.listener;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
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.hooks.BukkitAuthPlugin;
import com.github.games647.fastlogin.core.PlayerProfile;
import java.net.InetSocketAddress;
import java.util.UUID;
@ -25,6 +25,7 @@ public class ProtocolSupportListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onLoginStart(PlayerLoginStartEvent loginStartEvent) {
plugin.setServerStarted();
if (loginStartEvent.isLoginDenied()) {
return;
}
@ -32,28 +33,50 @@ public class ProtocolSupportListener implements Listener {
String username = loginStartEvent.getName();
//remove old data every time on a new login in order to keep the session only for one person
plugin.getSessions().remove(username);
plugin.getSessions().remove(loginStartEvent.getAddress().toString());
PlayerProfile playerProfile = plugin.getStorage().getProfile(username, true);
if (playerProfile != null) {
if (playerProfile.isPremium()) {
if (playerProfile.getUserId() != -1) {
startPremiumSession(username, loginStartEvent, true);
BukkitAuthPlugin authPlugin = plugin.getAuthPlugin();
if (authPlugin == null) {
return;
}
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(username);
if (profile != null) {
if (profile.getUserId() == -1) {
UUID premiumUUID = null;
if (plugin.getConfig().getBoolean("nameChangeCheck") || plugin.getConfig().getBoolean("autoRegister")) {
premiumUUID = plugin.getCore().getMojangApiConnector().getPremiumUUID(username);
}
} else if (playerProfile.getUserId() == -1) {
//user not exists in the db
BukkitAuthPlugin authPlugin = plugin.getAuthPlugin();
try {
if (plugin.getConfig().getBoolean("autoRegister") && !authPlugin.isRegistered(username)) {
UUID premiumUUID = plugin.getApiConnector().getPremiumUUID(username);
if (premiumUUID != null) {
plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
startPremiumSession(username, loginStartEvent, false);
if (premiumUUID != null && plugin.getConfig().getBoolean("nameChangeCheck")) {
profile = plugin.getCore().getStorage().loadProfile(premiumUUID);
if (profile != null) {
plugin.getLogger().log(Level.FINER, "Player {0} changed it's username", premiumUUID);
startPremiumSession(username, loginStartEvent, false, profile);
return;
}
}
if (premiumUUID != null
&& plugin.getConfig().getBoolean("autoRegister") && !authPlugin.isRegistered(username)) {
plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
startPremiumSession(username, loginStartEvent, false, profile);
return;
}
//no premium check passed so we save it as a cracked player
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
plugin.getSessions().put(loginStartEvent.getAddress().toString(), loginSession);
} catch (Exception ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to query isRegistered", ex);
}
} else if (profile.isPremium()) {
startPremiumSession(username, loginStartEvent, true, profile);
} else {
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
plugin.getSessions().put(loginStartEvent.getAddress().toString(), loginSession);
}
}
}
@ -63,19 +86,19 @@ public class ProtocolSupportListener implements Listener {
//skin was resolved -> premium player
if (propertiesResolveEvent.hasProperty("textures")) {
InetSocketAddress address = propertiesResolveEvent.getAddress();
PlayerSession session = plugin.getSessions().get(address.toString());
BukkitLoginSession session = plugin.getSessions().get(address.toString());
if (session != null) {
session.setVerified(true);
}
}
}
private void startPremiumSession(String playerName, PlayerLoginStartEvent loginStartEvent, boolean registered) {
private void startPremiumSession(String playerName, PlayerLoginStartEvent loginStartEvent, boolean registered
, PlayerProfile playerProfile) {
loginStartEvent.setOnlineMode(true);
InetSocketAddress address = loginStartEvent.getAddress();
PlayerSession playerSession = new PlayerSession(playerName, null, null);
playerSession.setRegistered(registered);
BukkitLoginSession playerSession = new BukkitLoginSession(playerName, null, null, registered, playerProfile);
plugin.getSessions().put(address.toString(), playerSession);
if (plugin.getConfig().getBoolean("premiumUuid")) {
loginStartEvent.setUseOnlineModeUUID(true);

View File

@ -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.ProtocolManager;
@ -9,9 +9,9 @@ import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.EncryptionUtil;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.PlayerSession;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
@ -73,13 +73,10 @@ public class EncryptionPacketListener extends PacketAdapter {
public void onPacketReceiving(PacketEvent packetEvent) {
Player player = packetEvent.getPlayer();
//the player name is unknown to ProtocolLib (so getName() doesn't work) - now uses ip:port as key
String uniqueSessionKey = player.getAddress().toString();
PlayerSession session = plugin.getSessions().get(uniqueSessionKey);
BukkitLoginSession session = plugin.getSessions().get(player.getAddress().toString());
if (session == null) {
disconnect(packetEvent, "Invalid request", Level.FINE
, "Player {0} tried to send encryption response at invalid state"
, player.getAddress());
disconnect(packetEvent, plugin.getCore().getMessage("invalid-requst"), true
, "Player {0} tried to send encryption response at invalid state", player.getAddress());
return;
}
@ -101,7 +98,7 @@ public class EncryptionPacketListener extends PacketAdapter {
String serverId = (new BigInteger(serverIdHash)).toString(16);
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);
session.setVerified(true);
@ -109,7 +106,7 @@ public class EncryptionPacketListener extends PacketAdapter {
receiveFakeStartPacket(username, player);
} else {
//user tried to fake a authentication
disconnect(packetEvent, "Invalid session", Level.FINE
disconnect(packetEvent, plugin.getCore().getMessage("invalid-session"), true
, "Player {0} ({1}) tried to log in with an invalid session ServerId: {2}"
, session.getUsername(), player.getAddress(), serverId);
}
@ -118,7 +115,7 @@ public class EncryptionPacketListener extends PacketAdapter {
packetEvent.setCancelled(true);
}
private void setPremiumUUID(PlayerSession session, Player player) {
private void setPremiumUUID(BukkitLoginSession session, Player player) {
UUID uuid = session.getUuid();
if (plugin.getConfig().getBoolean("premiumUuid") && uuid != null) {
try {
@ -132,7 +129,7 @@ public class EncryptionPacketListener extends PacketAdapter {
}
}
private boolean checkVerifyToken(PlayerSession session, PrivateKey privateKey, PacketEvent packetEvent) {
private boolean checkVerifyToken(BukkitLoginSession session, PrivateKey privateKey, PacketEvent packetEvent) {
byte[] requestVerify = session.getVerifyToken();
//encrypted verify token
byte[] responseVerify = packetEvent.getPacket().getByteArrays().read(1);
@ -140,9 +137,8 @@ public class EncryptionPacketListener extends PacketAdapter {
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L182
if (!Arrays.equals(requestVerify, EncryptionUtil.decryptData(privateKey, responseVerify))) {
//check if the verify token are equal to the server sent one
disconnect(packetEvent, "Invalid token", Level.FINE
, "Player {0} ({1}) tried to login with an invalid verify token. "
+ "Server: {2} Client: {3}"
disconnect(packetEvent, plugin.getCore().getMessage("invalid-verify-token"), true
, "Player {0} ({1}) tried to login with an invalid verify token. Server: {2} Client: {3}"
, session.getUsername(), packetEvent.getPlayer().getAddress(), requestVerify, responseVerify);
return false;
}
@ -171,23 +167,28 @@ public class EncryptionPacketListener extends PacketAdapter {
Object networkManager = getNetworkManager(player);
//try to detect the method by parameters
Method encryptConnectionMethod = FuzzyReflection.fromObject(networkManager)
.getMethodByParameters("a", SecretKey.class);
Method encryptConnectionMethod = FuzzyReflection
.fromObject(networkManager).getMethodByParameters("a", SecretKey.class);
//encrypt/decrypt following packets
//the client expects this behaviour
encryptConnectionMethod.invoke(networkManager, loginKey);
} catch (ReflectiveOperationException ex) {
disconnect(packetEvent, "Error occurred", Level.SEVERE, "Couldn't enable encryption", ex);
disconnect(packetEvent, plugin.getCore().getMessage("error-kick"), false, "Couldn't enable encryption", ex);
return false;
}
return true;
}
private void disconnect(PacketEvent packetEvent, String kickReason, Level logLevel, String logMessage
private void disconnect(PacketEvent packetEvent, String kickReason, boolean debugLevel, String logMessage
, Object... arguments) {
plugin.getLogger().log(logLevel, logMessage, arguments);
if (debugLevel) {
plugin.getLogger().log(Level.FINE, logMessage, arguments);
} else {
plugin.getLogger().log(Level.SEVERE, logMessage, arguments);
}
kickPlayer(packetEvent.getPlayer(), kickReason);
//cancel the event in order to prevent the server receiving an invalid packet
packetEvent.setCancelled(true);
@ -222,7 +223,7 @@ public class EncryptionPacketListener extends PacketAdapter {
} catch (InvocationTargetException | IllegalAccessException ex) {
plugin.getLogger().log(Level.WARNING, "Failed to fake a new start packet", ex);
//cancel the event in order to prevent the server receiving an invalid packet
kickPlayer(from, "Error occured");
kickPlayer(from, plugin.getCore().getMessage("error-kick"));
}
}
}

View File

@ -1,14 +1,14 @@
package com.github.games647.fastlogin.bukkit.listener;
package com.github.games647.fastlogin.bukkit.listener.packet;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
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.hooks.BukkitAuthPlugin;
import com.github.games647.fastlogin.core.PlayerProfile;
import java.lang.reflect.InvocationTargetException;
import java.security.PublicKey;
@ -61,7 +61,9 @@ public class StartPacketListener extends PacketAdapter {
*/
@Override
public void onPacketReceiving(PacketEvent packetEvent) {
final Player player = packetEvent.getPlayer();
plugin.setServerStarted();
Player player = packetEvent.getPlayer();
//this includes ip:port. Should be unique for an incoming login request with a timeout of 2 minutes
String sessionKey = player.getAddress().toString();
@ -71,38 +73,61 @@ public class StartPacketListener extends PacketAdapter {
//player.getName() won't work at this state
PacketContainer packet = packetEvent.getPacket();
String username = packet.getGameProfiles().read(0).getName();
plugin.getLogger().log(Level.FINER, "Player {0} with {1} connecting to the server"
, new Object[]{sessionKey, username});
PlayerProfile playerProfile = plugin.getStorage().getProfile(username, true);
if (playerProfile != null) {
if (playerProfile.isPremium()) {
if (playerProfile.getUserId() != -1) {
enablePremiumLogin(username, sessionKey, player, packetEvent, true);
BukkitAuthPlugin authPlugin = plugin.getAuthPlugin();
if (authPlugin == null) {
return;
}
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(username);
if (profile != null) {
if (profile.getUserId() == -1) {
UUID premiumUUID = null;
if (plugin.getConfig().getBoolean("nameChangeCheck") || plugin.getConfig().getBoolean("autoRegister")) {
premiumUUID = plugin.getCore().getMojangApiConnector().getPremiumUUID(username);
}
} else if (playerProfile.getUserId() == -1) {
//user not exists in the db
BukkitAuthPlugin authPlugin = plugin.getAuthPlugin();
try {
if (plugin.getConfig().getBoolean("autoRegister") && !authPlugin.isRegistered(username)) {
UUID premiumUUID = plugin.getApiConnector().getPremiumUUID(username);
if (premiumUUID != null) {
plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
enablePremiumLogin(username, sessionKey, player, packetEvent, false);
if (premiumUUID != null && plugin.getConfig().getBoolean("nameChangeCheck")) {
profile = plugin.getCore().getStorage().loadProfile(premiumUUID);
if (profile != null) {
plugin.getLogger().log(Level.FINER, "Player {0} changed it's username", premiumUUID);
enablePremiumLogin(username, profile, sessionKey, player, packetEvent, false);
return;
}
}
if (premiumUUID != null
&& plugin.getConfig().getBoolean("autoRegister") && !authPlugin.isRegistered(username)) {
plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
enablePremiumLogin(username, profile, sessionKey, player, packetEvent, false);
return;
}
//no premium check passed so we save it as a cracked player
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
plugin.getSessions().put(sessionKey, loginSession);
} catch (Exception ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to query isRegistered", ex);
}
} else if (profile.isPremium()) {
enablePremiumLogin(username, profile, sessionKey, player, packetEvent, true);
} else {
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
plugin.getSessions().put(sessionKey, loginSession);
}
}
}
//minecraft server implementation
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L161
private void enablePremiumLogin(String username, String sessionKey, Player player, PacketEvent packetEvent
, boolean registered) {
private void enablePremiumLogin(String username, PlayerProfile profile, String sessionKey, Player player
, PacketEvent packetEvent, boolean registered) {
//randomized server id to make sure the request is for our server
//this could be relevant http://www.sk89q.com/2011/09/minecraft-name-spoofing-exploit/
String serverId = Long.toString(random.nextLong(), 16);
@ -113,8 +138,8 @@ public class StartPacketListener extends PacketAdapter {
boolean success = sentEncryptionRequest(player, serverId, verifyToken);
if (success) {
PlayerSession playerSession = new PlayerSession(username, serverId, verifyToken);
playerSession.setRegistered(registered);
BukkitLoginSession playerSession = new BukkitLoginSession(username, serverId
, verifyToken, registered, profile);
plugin.getSessions().put(sessionKey, playerSession);
//cancel only if the player has a paid account otherwise login as normal offline player
packetEvent.setCancelled(true);

View File

@ -0,0 +1,71 @@
package com.github.games647.fastlogin.bukkit.tasks;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
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;
}
}

View File

@ -1,6 +1,10 @@
package com.github.games647.fastlogin.bukkit;
package com.github.games647.fastlogin.bukkit.tasks;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
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.ByteStreams;
@ -10,12 +14,11 @@ import java.util.concurrent.Future;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
public class ForceLoginTask implements Runnable {
protected final FastLoginBukkit plugin;
private final FastLoginBukkit plugin;
protected final Player player;
public ForceLoginTask(FastLoginBukkit plugin, Player player) {
@ -31,32 +34,24 @@ public class ForceLoginTask implements Runnable {
//remove the bungeecord identifier if there is ones
String id = '/' + player.getAddress().getAddress().getHostAddress() + ':' + player.getAddress().getPort();
PlayerSession session = plugin.getSessions().get(id);
BukkitAuthPlugin authPlugin = plugin.getAuthPlugin();
Storage storage = plugin.getStorage();
PlayerProfile playerProfile = null;
if (storage != null) {
playerProfile = storage.getProfile(player.getName(), false);
BukkitLoginSession session = plugin.getSessions().remove(id);
if (session == null) {
return;
}
if (session == null) {
//cracked player
if (playerProfile != null) {
playerProfile.setUuid(null);
playerProfile.setPremium(false);
storage.save(playerProfile);
}
//check if it's the same player as we checked before
} else if (player.getName().equals(session.getUsername())) {
Storage storage = plugin.getCore().getStorage();
PlayerProfile playerProfile = session.getProfile();
//check if it's the same player as we checked before
if (session.isVerified() && player.getName().equals(session.getUsername())) {
//premium player
BukkitAuthPlugin authPlugin = plugin.getAuthPlugin();
if (authPlugin == null) {
//maybe only bungeecord plugin
sendSuccessNotification();
} else {
boolean success = false;
if (isOnlineThreadSafe() && session.isVerified()) {
if (isOnlineThreadSafe()) {
if (session.needsRegistration()) {
success = forceRegister(authPlugin, player);
} else {
@ -76,23 +71,39 @@ public class ForceLoginTask implements Runnable {
sendSuccessNotification();
}
}
} else {
//cracked player
if (playerProfile != null) {
playerProfile.setUuid(null);
playerProfile.setPremium(false);
storage.save(playerProfile);
}
}
}
private boolean forceRegister(BukkitAuthPlugin authPlugin, Player player) {
plugin.getLogger().log(Level.FINE, "Register player {0}", player.getName());
String generatedPassword = plugin.generateStringPassword();
String generatedPassword = plugin.generateStringPassword(player);
boolean success = authPlugin.forceRegister(player, generatedPassword);
player.sendMessage(ChatColor.DARK_GREEN + "Auto registered with password: " + generatedPassword);
player.sendMessage(ChatColor.DARK_GREEN + "You may want change it?");
String message = plugin.getCore().getMessage("auto-register");
if (message != null) {
message = message.replace("%password", generatedPassword);
player.sendMessage(message);
}
return success;
}
private boolean forceLogin(BukkitAuthPlugin authPlugin, Player player) {
plugin.getLogger().log(Level.FINE, "Logging player {0} in", player.getName());
boolean success = authPlugin.forceLogin(player);
player.sendMessage(ChatColor.DARK_GREEN + "Auto logged in");
String message = plugin.getCore().getMessage("auto-login");
if (message != null) {
player.sendMessage(message);
}
return success;
}

View File

@ -11,18 +11,22 @@ description: |
website: ${project.url}
dev-url: ${project.url}
# Load the plugin as early as possible to inject it for all players
load: STARTUP
# Without Protocollib the plugin does not work at all
depend: [ProtocolLib]
softdepend:
- ProtocolSupport
# Auth plugins
- xAuth
- AuthMe
- CrazyLogin
- LoginSecurity
- RoyalAuth
- UltraAuth
# - xAuth
# - AuthMe
# - LogIt
# - CrazyLogin
# - LoginSecurity
# - RoyalAuth
# - UltraAuth
commands:
${project.parent.name}:
@ -31,9 +35,9 @@ commands:
usage: /<command> [player]
permission: ${project.artifactId}.command.premium
unpremium:
cracked:
description: 'Label the invoker or the player specified as cracked if he was marked premium before'
aliases: [cracked]
aliases: [unpremium]
usage: /<command> [player]
permission: ${project.artifactId}.command.unpremium
@ -47,11 +51,11 @@ permissions:
children:
${project.artifactId}.command.premium: true
${project.artifactId}.command.unpremium:
${project.artifactId}.command.cracked:
description: 'Label themselves as cracked'
default: true
${project.artifactId}.command..unpremium.other:
${project.artifactId}.command..cracked.other:
description: 'Label others as cracked'
children:
${project.artifactId}.command.unpremium: true
${project.artifactId}.command.cracked: true

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin</artifactId>
<version>1.1</version>
<version>1.5.1</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -37,6 +37,13 @@
</repositories>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>fastlogin.core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-proxy</artifactId>

View File

@ -0,0 +1,85 @@
package com.github.games647.fastlogin.bungee;
import com.github.games647.fastlogin.core.FastLoginCore;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.scheduler.GroupedThreadFactory;
import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider;
import net.md_5.bungee.config.YamlConfiguration;
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 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();
}
@Override
public void loadMessages() {
try {
saveDefaultFile("messages.yml");
File messageFile = new File(getDataFolder(), "messages.yml");
Configuration messageConfig = ConfigurationProvider.getProvider(YamlConfiguration.class).load(messageFile);
for (String key : messageConfig.getKeys()) {
String message = ChatColor.translateAlternateColorCodes('&', messageConfig.getString(key));
if (!message.isEmpty()) {
localeMessages.put(key, message);
}
}
} catch (IOException ex) {
getLogger().log(Level.SEVERE, "Failed to load messages", ex);
}
}
@Override
public void loadConfig() {
if (!getDataFolder().exists()) {
getDataFolder().mkdir();
}
saveDefaultFile("config.yml");
}
private void saveDefaultFile(String fileName) {
File configFile = new File(getDataFolder(), fileName);
if (!configFile.exists()) {
try (InputStream in = plugin.getResourceAsStream(fileName)) {
Files.copy(in, configFile.toPath());
} catch (IOException ioExc) {
getLogger().log(Level.SEVERE, "Error saving default " + fileName, ioExc);
}
}
}
}

View File

@ -2,19 +2,19 @@ package com.github.games647.fastlogin.bungee;
import com.github.games647.fastlogin.bungee.hooks.BungeeAuthHook;
import com.github.games647.fastlogin.bungee.hooks.BungeeAuthPlugin;
import com.github.games647.fastlogin.bungee.listener.PlayerConnectionListener;
import com.github.games647.fastlogin.bungee.listener.PluginMessageListener;
import com.github.games647.fastlogin.core.FastLoginCore;
import com.github.games647.fastlogin.core.LoginSession;
import com.google.common.cache.CacheBuilder;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
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.plugin.Plugin;
import net.md_5.bungee.config.Configuration;
@ -26,41 +26,29 @@ import net.md_5.bungee.config.YamlConfiguration;
*/
public class FastLoginBungee extends Plugin {
private static final char[] CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
private static final char[] PASSWORD_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
.toCharArray();
public static UUID parseId(String withoutDashes) {
return Util.getUUID(withoutDashes);
}
private final FastLoginCore loginCore = new BungeeCore(this);
private BungeeAuthPlugin bungeeAuthPlugin;
private final MojangApiConnector mojangApiConnector = new MojangApiConnector(this);
private Storage storage;
private Configuration configuration;
private final Random random = new Random();
private final ConcurrentMap<PendingConnection, Object> pendingAutoRegister = CacheBuilder
private final ConcurrentMap<PendingConnection, LoginSession> session = CacheBuilder
.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES)
.<PendingConnection, Object>build().asMap();
.expireAfterWrite(5, TimeUnit.MINUTES)
.<PendingConnection, LoginSession>build().asMap();
@Override
public void onEnable() {
if (!getDataFolder().exists()) {
getDataFolder().mkdir();
}
loginCore.setMojangApiConnector(new MojangApiBungee(loginCore));
File configFile = new File(getDataFolder(), "config.yml");
if (!configFile.exists()) {
try (InputStream in = getResourceAsStream("config.yml")) {
Files.copy(in, configFile.toPath());
} catch (IOException ioExc) {
getLogger().log(Level.SEVERE, "Error saving default config", ioExc);
}
}
loginCore.loadConfig();
loginCore.loadMessages();
try {
File configFile = new File(getDataFolder(), "config.yml");
configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(configFile);
String driver = configuration.getString("driver");
@ -70,14 +58,9 @@ public class FastLoginBungee extends Plugin {
String username = configuration.getString("username", "");
String password = configuration.getString("password", "");
storage = new Storage(this, driver, host, port, database, username, password);
try {
storage.createTables();
} catch (Exception ex) {
getLogger().log(Level.SEVERE, "Failed to setup database. Disabling plugin...", ex);
if (!loginCore.setupDatabase(driver, host, port, database, username, password)) {
return;
}
} catch (IOException ioExc) {
getLogger().log(Level.SEVERE, "Error loading config. Disabling plugin...", ioExc);
return;
@ -85,6 +68,7 @@ public class FastLoginBungee extends Plugin {
//events
getProxy().getPluginManager().registerListener(this, new PlayerConnectionListener(this));
getProxy().getPluginManager().registerListener(this, new PluginMessageListener(this));
//this is required to listen to messages from the server
getProxy().registerChannel(getDescription().getName());
@ -95,7 +79,7 @@ public class FastLoginBungee extends Plugin {
public String generateStringPassword() {
StringBuilder generatedPassword = new StringBuilder(8);
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();
@ -103,25 +87,23 @@ public class FastLoginBungee extends Plugin {
@Override
public void onDisable() {
if (storage != null) {
storage.close();
}
loginCore.close();
}
public Configuration getConfiguration() {
public FastLoginCore getCore() {
return loginCore;
}
public void setAuthPluginHook(BungeeAuthPlugin authPlugin) {
this.bungeeAuthPlugin = authPlugin;
}
public Configuration getConfig() {
return configuration;
}
public Storage getStorage() {
return storage;
}
public MojangApiConnector getMojangApiConnector() {
return mojangApiConnector;
}
public ConcurrentMap<PendingConnection, Object> getPendingAutoRegister() {
return pendingAutoRegister;
public ConcurrentMap<PendingConnection, LoginSession> getSession() {
return session;
}
/**

View File

@ -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");
}
}

View File

@ -1,204 +0,0 @@
package com.github.games647.fastlogin.bungee;
import com.github.games647.fastlogin.bungee.hooks.BungeeAuthPlugin;
import com.google.common.base.Charsets;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteStreams;
import java.lang.reflect.Field;
import java.util.UUID;
import java.util.logging.Level;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
import net.md_5.bungee.api.event.PluginMessageEvent;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.event.PreLoginEvent;
import net.md_5.bungee.api.event.ServerConnectedEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.connection.InitialHandler;
import net.md_5.bungee.connection.LoginResult;
import net.md_5.bungee.connection.LoginResult.Property;
import net.md_5.bungee.event.EventHandler;
/**
* Enables online mode logins for specified users and sends
* plugin message to the Bukkit version of this plugin in
* order to clear that the connection is online mode.
*/
public class PlayerConnectionListener implements Listener {
protected final FastLoginBungee plugin;
public PlayerConnectionListener(FastLoginBungee plugin) {
this.plugin = plugin;
}
@EventHandler
public void onPreLogin(final PreLoginEvent preLoginEvent) {
if (preLoginEvent.isCancelled()) {
return;
}
preLoginEvent.registerIntent(plugin);
ProxyServer.getInstance().getScheduler().runAsync(plugin, new Runnable() {
@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
public void onLogin(PostLoginEvent loginEvent) {
ProxiedPlayer player = loginEvent.getPlayer();
PendingConnection connection = player.getPendingConnection();
String username = connection.getName();
if (connection.isOnlineMode()) {
PlayerProfile playerProfile = plugin.getStorage().getProfile(player.getName(), false);
playerProfile.setUuid(player.getUniqueId());
//bungeecord will do this automatically so override it on disabled option
InitialHandler initialHandler = (InitialHandler) connection;
if (!plugin.getConfiguration().getBoolean("premiumUuid")) {
try {
UUID offlineUUID = UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(Charsets.UTF_8));
Field idField = initialHandler.getClass().getDeclaredField("uniqueId");
idField.setAccessible(true);
idField.set(connection, offlineUUID);
//bungeecord doesn't support overriding the premium uuid
// connection.setUniqueId(offlineUUID);
} catch (NoSuchFieldException | IllegalAccessException ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to set offline uuid", ex);
}
}
if (!plugin.getConfiguration().getBoolean("forwardSkin")) {
//this is null on offline mode
LoginResult loginProfile = initialHandler.getLoginProfile();
if (loginProfile != null) {
loginProfile.setProperties(new Property[]{});
}
}
}
}
@EventHandler
public void onServerConnected(ServerConnectedEvent serverConnectedEvent) {
ProxiedPlayer player = serverConnectedEvent.getPlayer();
ForceLoginTask loginTask = new ForceLoginTask(plugin, player, serverConnectedEvent.getServer());
ProxyServer.getInstance().getScheduler().runAsync(plugin, loginTask);
}
@EventHandler
public void onPluginMessage(PluginMessageEvent pluginMessageEvent) {
String channel = pluginMessageEvent.getTag();
if (pluginMessageEvent.isCancelled() || !plugin.getDescription().getName().equals(channel)) {
return;
}
//the client shouldn't be able to read the messages in order to know something about server internal states
//moreover the client shouldn't be able fake a running premium check by sending the result message
pluginMessageEvent.setCancelled(true);
//check if the message is sent from the server
if (Server.class.isAssignableFrom(pluginMessageEvent.getSender().getClass())) {
byte[] data = pluginMessageEvent.getData();
ByteArrayDataInput dataInput = ByteStreams.newDataInput(data);
String subchannel = dataInput.readUTF();
final ProxiedPlayer forPlayer = (ProxiedPlayer) pluginMessageEvent.getReceiver();
if ("ON".equals(subchannel)) {
ProxyServer.getInstance().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
PlayerProfile playerProfile = plugin.getStorage().getProfile(forPlayer.getName(), 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);
}
});
} else if ("OFF".equals(subchannel)) {
ProxyServer.getInstance().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
PlayerProfile playerProfile = plugin.getStorage().getProfile(forPlayer.getName(), 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);
//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 ("SUCCESS".equals(subchannel)) {
if (forPlayer.getPendingConnection().isOnlineMode()) {
//bukkit module successfully received and force logged in the user
//update only on success to prevent corrupt data
PlayerProfile playerProfile = plugin.getStorage().getProfile(forPlayer.getName(), false);
playerProfile.setPremium(true);
//we override this in the loginevent
// playerProfile.setUuid(forPlayer.getUniqueId());
plugin.getStorage().save(playerProfile);
TextComponent textComponent = new TextComponent("Removed to the list of premium players");
textComponent.setColor(ChatColor.DARK_GREEN);
forPlayer.sendMessage(textComponent);
}
}
}
}
}

View File

@ -1,201 +0,0 @@
package com.github.games647.fastlogin.bungee;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
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;
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);
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);
}
}
}
}

View File

@ -25,14 +25,14 @@ public class BungeeAuthHook implements BungeeAuthPlugin {
private final Tables databaseConnection = new Tables();
@Override
public boolean forceLogin(final ProxiedPlayer player) {
public boolean forceLogin(ProxiedPlayer player) {
//https://github.com/MatteCarra/BungeeAuth/blob/master/src/me/vik1395/BungeeAuth/Login.java#L92-95
Main.plonline.add(player.getName());
//renamed from ct to databaseConnection
// databaseConnection.setStatus(player.getName(), "online");
final Class<?>[] parameterTypes = new Class<?>[]{String.class, String.class};
final Object[] arguments = new Object[]{player.getName(), "online"};
Class<?>[] parameterTypes = new Class<?>[]{String.class, String.class};
Object[] arguments = new Object[]{player.getName(), "online"};
try {
callProtected("setStatus", parameterTypes, arguments);
@ -56,7 +56,7 @@ public class BungeeAuthHook implements BungeeAuthPlugin {
}
@Override
public boolean forceRegister(final ProxiedPlayer player, String password) {
public boolean forceRegister(ProxiedPlayer player, String password) {
//https://github.com/MatteCarra/BungeeAuth/blob/master/src/me/vik1395/BungeeAuth/Register.java#L102
PasswordHandler ph = new PasswordHandler();
Random rand = new Random();
@ -77,9 +77,9 @@ public class BungeeAuthHook implements BungeeAuthPlugin {
//renamed t to databaseConnection
// databaseConnection.newPlayerEntry(player.getName(), hash, pType, "", lastip, regdate, lastip, lastseen);
final Class<?>[] parameterTypes = new Class<?>[] {String.class, String.class, String.class, String.class
Class<?>[] parameterTypes = new Class<?>[] {String.class, String.class, String.class, String.class
, String.class, String.class, String.class, String.class};
final Object[] arguments = new Object[] {player.getName(), hash, pType, "", lastip, regdate, lastip, lastseen};
Object[] arguments = new Object[] {player.getName(), hash, pType, "", lastip, regdate, lastip, lastseen};
try {
callProtected("newPlayerEntry", parameterTypes, arguments);

View File

@ -0,0 +1,91 @@
package com.github.games647.fastlogin.bungee.listener;
import com.github.games647.fastlogin.bungee.tasks.AsyncPremiumCheck;
import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.bungee.tasks.ForceLoginTask;
import com.github.games647.fastlogin.core.LoginSession;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.google.common.base.Charsets;
import java.lang.reflect.Field;
import java.util.UUID;
import java.util.logging.Level;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.event.PreLoginEvent;
import net.md_5.bungee.api.event.ServerConnectedEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.connection.InitialHandler;
import net.md_5.bungee.connection.LoginResult;
import net.md_5.bungee.connection.LoginResult.Property;
import net.md_5.bungee.event.EventHandler;
/**
* Enables online mode logins for specified users and sends
* plugin message to the Bukkit version of this plugin in
* order to clear that the connection is online mode.
*/
public class PlayerConnectionListener implements Listener {
protected final FastLoginBungee plugin;
public PlayerConnectionListener(FastLoginBungee plugin) {
this.plugin = plugin;
}
@EventHandler
public void onPreLogin(PreLoginEvent preLoginEvent) {
if (preLoginEvent.isCancelled()) {
return;
}
preLoginEvent.registerIntent(plugin);
ProxyServer.getInstance().getScheduler().runAsync(plugin, new AsyncPremiumCheck(plugin, preLoginEvent));
}
@EventHandler
public void onLogin(PostLoginEvent loginEvent) {
ProxiedPlayer player = loginEvent.getPlayer();
PendingConnection connection = player.getPendingConnection();
String username = connection.getName();
if (connection.isOnlineMode()) {
LoginSession session = plugin.getSession().get(connection);
PlayerProfile playerProfile = session.getProfile();
playerProfile.setUuid(player.getUniqueId());
//bungeecord will do this automatically so override it on disabled option
InitialHandler initialHandler = (InitialHandler) connection;
if (!plugin.getConfig().getBoolean("premiumUuid")) {
try {
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");
idField.setAccessible(true);
idField.set(connection, offlineUUID);
} catch (NoSuchFieldException | IllegalAccessException ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to set offline uuid", ex);
}
}
if (!plugin.getConfig().getBoolean("forwardSkin")) {
//this is null on offline mode
LoginResult loginProfile = initialHandler.getLoginProfile();
if (loginProfile != null) {
loginProfile.setProperties(new Property[]{});
}
}
}
}
@EventHandler
public void onServerConnected(ServerConnectedEvent serverConnectedEvent) {
ProxiedPlayer player = serverConnectedEvent.getPlayer();
ForceLoginTask loginTask = new ForceLoginTask(plugin, player, serverConnectedEvent.getServer());
ProxyServer.getInstance().getScheduler().runAsync(plugin, loginTask);
}
}

View File

@ -0,0 +1,68 @@
package com.github.games647.fastlogin.bungee.listener;
import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.bungee.tasks.AsyncToggleMessage;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteStreams;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
import net.md_5.bungee.api.event.PluginMessageEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
public class PluginMessageListener implements Listener {
protected final FastLoginBungee plugin;
public PluginMessageListener(FastLoginBungee plugin) {
this.plugin = plugin;
}
@EventHandler
public void onPluginMessage(PluginMessageEvent pluginMessageEvent) {
String channel = pluginMessageEvent.getTag();
if (pluginMessageEvent.isCancelled() || !plugin.getDescription().getName().equals(channel)) {
return;
}
//the client shouldn't be able to read the messages in order to know something about server internal states
//moreover the client shouldn't be able fake a running premium check by sending the result message
pluginMessageEvent.setCancelled(true);
//check if the message is sent from the server
if (Server.class.isAssignableFrom(pluginMessageEvent.getSender().getClass())) {
readMessage(pluginMessageEvent);
}
}
private void readMessage(PluginMessageEvent pluginMessageEvent) {
byte[] data = pluginMessageEvent.getData();
ByteArrayDataInput dataInput = ByteStreams.newDataInput(data);
String subchannel = dataInput.readUTF();
ProxiedPlayer fromPlayer = (ProxiedPlayer) pluginMessageEvent.getReceiver();
if ("ON".equals(subchannel)) {
String playerName = dataInput.readUTF();
AsyncToggleMessage task = new AsyncToggleMessage(plugin, fromPlayer, playerName, true);
ProxyServer.getInstance().getScheduler().runAsync(plugin, task);
} else if ("OFF".equals(subchannel)) {
String playerName = dataInput.readUTF();
AsyncToggleMessage task = new AsyncToggleMessage(plugin, fromPlayer, playerName, false);
ProxyServer.getInstance().getScheduler().runAsync(plugin, task);
} else if ("SUCCESS".equals(subchannel)) {
if (fromPlayer.getPendingConnection().isOnlineMode()) {
//bukkit module successfully received and force logged in the user
//update only on success to prevent corrupt data
PlayerProfile playerProfile = plugin.getCore().getStorage().loadProfile(fromPlayer.getName());
playerProfile.setPremium(true);
//we override this in the loginevent
plugin.getCore().getStorage().save(playerProfile);
}
}
}
}

View File

@ -0,0 +1,92 @@
package com.github.games647.fastlogin.bungee.tasks;
import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.bungee.hooks.BungeeAuthPlugin;
import com.github.games647.fastlogin.core.LoginSession;
import com.github.games647.fastlogin.core.PlayerProfile;
import java.util.UUID;
import java.util.logging.Level;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.event.PreLoginEvent;
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();
plugin.getSession().remove(connection);
String username = connection.getName();
try {
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(username);
if (profile == null) {
return;
}
if (profile.getUserId() == -1) {
UUID premiumUUID = null;
if (plugin.getConfig().getBoolean("nameChangeCheck") || plugin.getConfig().getBoolean("autoRegister")) {
premiumUUID = plugin.getCore().getMojangApiConnector().getPremiumUUID(username);
}
if (premiumUUID == null
|| checkNameChange(premiumUUID, connection, username)
|| checkPremiumName(username, connection, profile)) {
//nothing detected the player as premium -> start a cracked session
plugin.getSession().put(connection, new LoginSession(username, false, profile));
}
} else if (profile.isPremium()) {
requestPremiumLogin(connection, profile, username, true);
} else {
plugin.getSession().put(connection, new LoginSession(username, false, profile));
}
} catch (Exception ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to check premium state", ex);
} finally {
preLoginEvent.completeIntent(plugin);
}
}
private boolean checkPremiumName(String username, PendingConnection connection, PlayerProfile profile)
throws Exception {
BungeeAuthPlugin authPlugin = plugin.getBungeeAuthPlugin();
if (plugin.getConfig().getBoolean("autoRegister")
&& (authPlugin == null || !authPlugin.isRegistered(username))) {
plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
requestPremiumLogin(connection, profile, username, false);
return true;
}
return false;
}
private boolean checkNameChange(UUID premiumUUID, PendingConnection connection, String username) {
//user not exists in the db
if (plugin.getConfig().getBoolean("nameChangeCheck")) {
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(premiumUUID);
if (profile != null) {
//uuid exists in the database
plugin.getLogger().log(Level.FINER, "Player {0} changed it's username", premiumUUID);
requestPremiumLogin(connection, profile, username, false);
return true;
}
}
return false;
}
private void requestPremiumLogin(PendingConnection con, PlayerProfile profile, String username, boolean register) {
con.setOnlineMode(true);
plugin.getSession().put(con, new LoginSession(username, register, profile));
}
}

View File

@ -0,0 +1,57 @@
package com.github.games647.fastlogin.bungee.tasks;
import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.core.PlayerProfile;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
public class AsyncToggleMessage implements Runnable {
private final FastLoginBungee plugin;
private final ProxiedPlayer fromPlayer;
private final String targetPlayer;
private final boolean toPremium;
public AsyncToggleMessage(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().loadProfile(targetPlayer);
if (!playerProfile.isPremium()) {
fromPlayer.sendMessage(TextComponent.fromLegacyText(plugin.getCore().getMessage("not-premium")));
return;
}
playerProfile.setPremium(false);
playerProfile.setUuid(null);
plugin.getCore().getStorage().save(playerProfile);
fromPlayer.sendMessage(TextComponent.fromLegacyText(plugin.getCore().getMessage("remove-premium")));
}
private void activatePremium() {
PlayerProfile playerProfile = plugin.getCore().getStorage().loadProfile(targetPlayer);
if (playerProfile.isPremium()) {
fromPlayer.sendMessage(TextComponent.fromLegacyText(plugin.getCore().getMessage("already-exists")));
return;
}
playerProfile.setPremium(true);
plugin.getCore().getStorage().save(playerProfile);
fromPlayer.sendMessage(TextComponent.fromLegacyText(plugin.getCore().getMessage("add-premium")));
}
}

View File

@ -1,11 +1,15 @@
package com.github.games647.fastlogin.bungee;
package com.github.games647.fastlogin.bungee.tasks;
import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.bungee.hooks.BungeeAuthPlugin;
import com.github.games647.fastlogin.core.LoginSession;
import com.github.games647.fastlogin.core.PlayerProfile;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import java.util.UUID;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
@ -23,17 +27,19 @@ public class ForceLoginTask implements Runnable {
@Override
public void run() {
PlayerProfile playerProfile = plugin.getStorage().getProfile(player.getName(), false);
PendingConnection pendingConnection = player.getPendingConnection();
LoginSession session = plugin.getSession().remove(pendingConnection);
PlayerProfile playerProfile = session.getProfile();
//force login only on success
if (player.getPendingConnection().isOnlineMode()) {
boolean autoRegister = plugin.getPendingAutoRegister().remove(player.getPendingConnection()) != null;
if (pendingConnection.isOnlineMode()) {
boolean autoRegister = session.needsRegistration();
BungeeAuthPlugin authPlugin = plugin.getBungeeAuthPlugin();
if (authPlugin == null) {
sendBukkitLoginNotification(autoRegister);
} else if (player.isConnected()) {
if (autoRegister) {
if (session.needsRegistration()) {
String password = plugin.generateStringPassword();
if (authPlugin.forceRegister(player, password)) {
sendBukkitLoginNotification(autoRegister);
@ -44,9 +50,8 @@ public class ForceLoginTask implements Runnable {
}
} else {
//cracked player
//update only on success to prevent corrupt data
playerProfile.setPremium(false);
plugin.getStorage().save(playerProfile);
plugin.getCore().getStorage().save(playerProfile);
}
}

32
core/pom.xml Normal file
View 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.5.1</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>

View File

@ -0,0 +1,67 @@
package com.github.games647.fastlogin.core;
import java.io.File;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
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));
}
protected final Map<String, String> localeMessages = new ConcurrentHashMap<>();
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 ThreadFactory getThreadFactory();
public String getMessage(String key) {
return localeMessages.get(key);
}
public abstract void loadMessages();
public abstract void loadConfig();
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();
}
}
}

View File

@ -0,0 +1,31 @@
package com.github.games647.fastlogin.core;
public class LoginSession {
private final String username;
private final boolean registered;
private final PlayerProfile profile;
public LoginSession(String username, boolean registered, PlayerProfile profile) {
this.username = username;
this.registered = registered;
this.profile = profile;
}
public String getUsername() {
return username;
}
/**
* This value is always false if we authenticate the player with a cracked authentication
*
* @return
*/
public boolean needsRegistration() {
return !registered;
}
public PlayerProfile getProfile() {
return profile;
}
}

View File

@ -1,6 +1,4 @@
package com.github.games647.fastlogin.bungee;
import com.google.gson.Gson;
package com.github.games647.fastlogin.core;
import java.io.BufferedReader;
import java.io.IOException;
@ -11,17 +9,12 @@ import java.util.UUID;
import java.util.logging.Level;
import java.util.regex.Pattern;
import net.md_5.bungee.BungeeCord;
public class MojangApiConnector {
public abstract class MojangApiConnector {
//http connection, read timeout and user agent for a connection to mojang api servers
private static final int TIMEOUT = 1 * 1_000;
private static final String USER_AGENT = "Premium-Checker";
//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_
@ -30,11 +23,9 @@ public class MojangApiConnector {
//compile the pattern only on plugin enable -> and this have to be threadsafe
private final Pattern playernameMatcher = Pattern.compile(VALID_PLAYERNAME);
private final FastLoginBungee plugin;
protected final FastLoginCore plugin;
private final Gson gson = new Gson();
public MojangApiConnector(FastLoginBungee plugin) {
public MojangApiConnector(FastLoginCore plugin) {
this.plugin = plugin;
}
@ -53,8 +44,7 @@ public class MojangApiConnector {
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line = reader.readLine();
if (line != null && !line.equals("null")) {
MojangPlayer mojangPlayer = BungeeCord.getInstance().gson.fromJson(line, MojangPlayer.class);
return FastLoginBungee.parseId(mojangPlayer.getId());
return getUUIDFromJson(line);
}
}
//204 - no content for not found
@ -67,10 +57,14 @@ public class MojangApiConnector {
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();
connection.setConnectTimeout(TIMEOUT);
connection.setReadTimeout(TIMEOUT);
connection.setReadTimeout(2 * TIMEOUT);
//the new Mojang API just uses json as response
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("User-Agent", USER_AGENT);

View File

@ -1,4 +1,4 @@
package com.github.games647.fastlogin.bungee;
package com.github.games647.fastlogin.core;
import java.util.UUID;
@ -40,7 +40,7 @@ public class PlayerProfile {
return userId;
}
protected synchronized void setUserId(long generatedId) {
public synchronized void setUserId(long generatedId) {
this.userId = generatedId;
}

View File

@ -0,0 +1,241 @@
package com.github.games647.fastlogin.core;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.UUID;
import java.util.logging.Level;
public class Storage {
private static final String PREMIUM_TABLE = "premium";
private final FastLoginCore core;
private final HikariDataSource dataSource;
public Storage(FastLoginCore core, String driver, String host, int port, String databasePath
, String user, String pass) {
this.core = core;
HikariConfig databaseConfig = new HikariConfig();
databaseConfig.setUsername(user);
databaseConfig.setPassword(pass);
databaseConfig.setDriverClassName(driver);
databaseConfig.setThreadFactory(core.getThreadFactory());
databasePath = databasePath.replace("{pluginDir}", core.getDataFolder().getAbsolutePath());
databaseConfig.setThreadFactory(core.getThreadFactory());
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;
Statement createStmt = null;
try {
con = dataSource.getConnection();
createStmt = 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");
}
createStmt.executeUpdate(createDataStmt);
} finally {
closeQuietly(con);
closeQuietly(createStmt);
}
}
public PlayerProfile loadProfile(String name) {
Connection con = null;
PreparedStatement loadStmt = null;
ResultSet resultSet = null;
try {
con = dataSource.getConnection();
loadStmt = con.prepareStatement("SELECT * FROM " + PREMIUM_TABLE + " WHERE Name=?");
loadStmt.setString(1, name);
resultSet = loadStmt.executeQuery();
if (resultSet.next()) {
long userId = resultSet.getInt(1);
String unparsedUUID = resultSet.getString(2);
UUID uuid;
if (unparsedUUID == null) {
uuid = null;
} else {
uuid = FastLoginCore.parseId(unparsedUUID);
}
boolean premium = resultSet.getBoolean(4);
String lastIp = resultSet.getString(5);
long lastLogin = resultSet.getTimestamp(6).getTime();
PlayerProfile playerProfile = new PlayerProfile(userId, uuid, name, premium, lastIp, lastLogin);
return playerProfile;
} else {
PlayerProfile crackedProfile = new PlayerProfile(null, name, false, "");
return crackedProfile;
}
} catch (SQLException sqlEx) {
core.getLogger().log(Level.SEVERE, "Failed to query profile", sqlEx);
} finally {
closeQuietly(con);
closeQuietly(loadStmt);
closeQuietly(resultSet);
}
return null;
}
public PlayerProfile loadProfile(UUID uuid) {
Connection con = null;
PreparedStatement loadStmt = null;
ResultSet resultSet = null;
try {
con = dataSource.getConnection();
loadStmt = con.prepareStatement("SELECT * FROM " + PREMIUM_TABLE + " WHERE UUID=?");
loadStmt.setString(1, uuid.toString().replace("-", ""));
resultSet = loadStmt.executeQuery();
if (resultSet.next()) {
long userId = resultSet.getInt(1);
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);
return playerProfile;
}
} catch (SQLException sqlEx) {
core.getLogger().log(Level.SEVERE, "Failed to query profile", sqlEx);
} finally {
closeQuietly(con);
closeQuietly(loadStmt);
closeQuietly(resultSet);
}
return null;
}
public boolean save(PlayerProfile playerProfile) {
Connection con = null;
PreparedStatement updateStmt = null;
PreparedStatement saveStmt = null;
ResultSet generatedKeys = null;
try {
con = dataSource.getConnection();
UUID uuid = playerProfile.getUuid();
if (playerProfile.getUserId() == -1) {
//User was authenticated with a premium authentication, so it's possible that the player is premium
if (uuid != null) {
updateStmt = con.prepareStatement("UPDATE " + PREMIUM_TABLE
+ " SET NAME=?, LastIp=?, LastLogin=CURRENT_TIMESTAMP"
+ " WHERE UUID=? AND PREMIUM=1");
updateStmt.setString(1, playerProfile.getPlayerName());
updateStmt.setString(2, playerProfile.getLastIp());
updateStmt.setString(3, uuid.toString().replace("-", ""));
int affectedRows = updateStmt.executeUpdate();
if (affectedRows > 0) {
//username changed and we updated the existing database record
//so we don't need to run an insert
return true;
}
}
saveStmt = con.prepareStatement("INSERT INTO " + PREMIUM_TABLE
+ " (UUID, Name, Premium, LastIp) VALUES (?, ?, ?, ?) "
, Statement.RETURN_GENERATED_KEYS);
if (uuid == null) {
saveStmt.setString(1, null);
} else {
saveStmt.setString(1, uuid.toString().replace("-", ""));
}
saveStmt.setString(2, playerProfile.getPlayerName());
saveStmt.setBoolean(3, playerProfile.isPremium());
saveStmt.setString(4, playerProfile.getLastIp());
saveStmt.execute();
generatedKeys = saveStmt.getGeneratedKeys();
if (generatedKeys != null && generatedKeys.next()) {
playerProfile.setUserId(generatedKeys.getInt(1));
}
} else {
saveStmt = con.prepareStatement("UPDATE " + PREMIUM_TABLE
+ " SET UUID=?, Name=?, Premium=?, LastIp=?, LastLogin=CURRENT_TIMESTAMP WHERE UserID=?");
if (uuid == null) {
saveStmt.setString(1, null);
} else {
saveStmt.setString(1, uuid.toString().replace("-", ""));
}
saveStmt.setString(2, playerProfile.getPlayerName());
saveStmt.setBoolean(3, playerProfile.isPremium());
saveStmt.setString(4, playerProfile.getLastIp());
saveStmt.setLong(5, playerProfile.getUserId());
saveStmt.execute();
}
return true;
} catch (SQLException ex) {
core.getLogger().log(Level.SEVERE, "Failed to save playerProfile", ex);
} finally {
closeQuietly(con);
closeQuietly(updateStmt);
closeQuietly(saveStmt);
closeQuietly(generatedKeys);
}
return false;
}
public void close() {
dataSource.close();
}
private void closeQuietly(AutoCloseable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (Exception closeEx) {
core.getLogger().log(Level.SEVERE, "Failed to close connection", closeEx);
}
}
}
}

View File

@ -0,0 +1,36 @@
package com.github.games647.fastlogin.core.importer;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
public class AutoInImporter extends Importer {
private static final String USER_TABLE = "nicknames";
private static final String UUID_TABLE = "uuids";
private static final String SESSION_TABLE = "sessions";
@Override
public int importData(DataSource source, DataSource target, String targetTable) throws SQLException {
Connection con = null;
Statement stmt = null;
try {
con = source.getConnection();
stmt = con.createStatement();
int importedRows = stmt.executeUpdate("INSERT INTO " + targetTable + " SELECT"
+ " name AS Name,"
+ " enabledLogin AS Premium,"
+ " '' AS LastIp,"
+ " REPLACE(puuid, '-', '') AS UUID"
+ " FROM " + USER_TABLE
+ " JOIN " + UUID_TABLE
+ " ON " + UUID_TABLE + ".id = " + UUID_TABLE + ".nickname_id");
return importedRows;
} finally {
closeQuietly(stmt);
closeQuietly(con);
}
}
}

View File

@ -0,0 +1,33 @@
package com.github.games647.fastlogin.core.importer;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
public class BPAImporter extends Importer {
private static final String DEFAULT_TABLE_NAME = "users";
@Override
public int importData(DataSource source, DataSource target, String targetTable) throws SQLException {
Connection con = null;
Statement stmt = null;
try {
con = source.getConnection();
stmt = con.createStatement();
int importedRows = stmt.executeUpdate("INSERT INTO " + targetTable + " SELECT"
+ " nick AS Name,"
+ " NULL AS UUID,"
+ " checked AS Premium,"
+ " lastIP AS LastIp,"
+ " FROM_UNIXTIME(lastJoined * 0.001) AS LastLogin"
+ " FROM " + DEFAULT_TABLE_NAME);
return importedRows;
} finally {
closeQuietly(stmt);
closeQuietly(con);
}
}
}

View File

@ -0,0 +1,33 @@
package com.github.games647.fastlogin.core.importer;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
public class ElDziAuthImporter extends Importer {
private static final String TABLE_NAME = "accounts";
@Override
public int importData(DataSource source, DataSource target, String targetTable) throws SQLException {
Connection con = null;
Statement stmt = null;
try {
con = source.getConnection();
stmt = con.createStatement();
int importedRows = stmt.executeUpdate("INSERT INTO " + targetTable + " SELECT"
+ " nick AS Name,"
+ " uuid AS UUID,"
+ " premium AS Premium,"
+ " lastIp AS LastIp,"
+ " FROM_UNIXTIME(lastPlayed * 0.001) AS LastLogin"
+ " FROM " + TABLE_NAME);
return importedRows;
} finally {
closeQuietly(stmt);
closeQuietly(con);
}
}
}

View File

@ -0,0 +1,20 @@
package com.github.games647.fastlogin.core.importer;
import java.sql.SQLException;
import javax.sql.DataSource;
public abstract class Importer {
public abstract int importData(DataSource source, DataSource target, String targetTable) throws SQLException;
protected void closeQuietly(AutoCloseable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (Exception ignore) {
//ignore
}
}
}
}

View File

@ -3,7 +3,7 @@
# Source code: https://github.com/games647/FastLogin
#
# 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
#
@ -40,6 +40,50 @@ autoRegister: false
# This feature requires Cauldron, Spigot or a fork of Spigot (PaperSpigot, TacoSpigot)
premiumUuid: false
# This will make an additional check (only for player names which are not in the database) against the mojang servers
# in order to get the premium UUID. If that premium UUID is in the database, we can assume on sucessful login that the
# player changed it's username and we just update the name in the database.
# Examples:
# #### Case 1
# nameChangeCheck = false ----- autoRegister = false
#
# Player logins as cracked until the player invoked the command /premium. Then we could override the existing database
# record.
#
# #### Case 2
#
# nameChangeCheck = true ----- autoRegister = false
#
# Connect the Mojang API and check what UUID the player has (UUID exists => Paid Minecraft account). If that UUID is in
# the database it's an **existing player** and FastLogin can **assume** the player is premium and changed the username.
# If it's not in the database, it's a new player and **could be a cracked player**. So we just use a offline mode
# authentication for this player.
#
# **Limitation**: Cracked players who uses the new username of a paid account cannot join the server if the database
# contains the old name. (Example: The owner of the paid account no longer plays on the server, but changed the username
# in the meanwhile).
#
# #### Case 3
#
# nameChangeCheck = false ----- autoRegister = true
#
# We will always request a premium authentication if the username is unknown to us, but is in use by a paid minecraft
# account. This means it's kind of a more aggressive check like nameChangeCheck = true and autoRegister = false, because
# it request a premium authentication which are completely new to us, that even the premium UUID is not in our database.
#
# **Limitation**: see below
#
# #### Case 4
#
# nameChangeCheck = true ----- autoRegister = true
#
# Based on autoRegister it checks if the player name is premium and login using a premium authentication. After that
# fastlogin receives the premium UUID and can update the database record.
#
# **Limitation from autoRegister**: New offline players who uses the username of an existing minecraft cannot join the
# server.
nameChangeCheck: false
# If your players have a premium account and a skin associated to their account, this plugin
# can download the data and set it to the online player.
#
@ -64,7 +108,7 @@ driver: org.sqlite.JDBC
# File location
database: '{pluginDir}/FastLogin.db'
# MySQL and SQLite
# MySQL
#driver: com.mysql.jdbc.Driver
#host: localhost
#port: 3306

View File

@ -0,0 +1,79 @@
# 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
#
# You want to have language template? Visit the Github Wiki here:
# https://github.com/games647/FastLogin/wiki/English
# In order to split a message into seperate lines you could just make a new line, but keep the '
# Example:
# bla: '&aFirst line
# Second line
# Third line'
# If you want to disable a message, you can just set it to a empty value.
# In this case no message will be sent
# Example:
# bla: ''
# ========= Shared (BungeeCord and Bukkit) ============
# Player activated premium logins in order to skip offline authentication
add-premium: '&2Added to the list of premium players'
# Player is already set be a paid account
already-exists: '&4You are already on the premium list'
# Player was changed to be cracked
remove-premium: '&2Removed from the list of premium players'
# Player is already set to be cracked
not-premium: '&4You are not in the premium list'
# Admin wanted to change the premium of a user that isn't known to the plugin
player-unknown: '&4Player not in the database'
# ========= Bukkit/Spigot/PaperSpigot/TacoSpigot only ================================
# The user skipped the authentication, because it was a premium player
auto-login: '&2Auto logged in'
# The user was auto registered on the first join. The user account will be registered to protect it from cracked players
# The password can be used if the mojang servers are down and you still want your premium users to login (PLANNED)
auto-register: '&2Auto registered with password: %password
You may want change it?'
# Player is not able to toggle the premium state of other players
no-permission: '&4Not enough permissions'
# Although the console can toggle the premium state, it's not possible for the console itself.
# Because the console is not a user. (obviously, isn't it?)
no-console: '&4You are not a player. You cannot toggle the premium state for YOURSELF as a console'
# The user wants to toggle premium state, but BungeeCord support is enabled. This means the server have to communicate
# with the BungeeCord first which will establish a connection with the database server.
wait-on-proxy: '&6Sending request...'
# When ProtocolLib is enabled and the plugin is unable to continue handling a login request after a requested premium
# authentication. In this state the client expects a success packet with a encrypted connection or disconnect packet.
# So we kick the player, if we cannot encrypt the connection. In other situation (example: premium name check),
# the player will be just authenticated as cracked
error-kick: '&4Error occured'
# The server sents a verify token within the premium authentication reqest. If this doesn't match on response,
# it could be another client sending malicious packets
invalid-verify-token: '&4Invalid token'
# The client sent no request join server request to the mojang servers which would proof that it's owner of that
# acciunt. Only modified clients would do this.
invalid-session: '&4Invalid session'
# The client sent a malicous packet without a login request packet
invalid-requst: '&4Invalid request'
# ========= Bungee/Waterfall only ================================

21
pom.xml
View File

@ -8,7 +8,7 @@
<packaging>pom</packaging>
<name>FastLogin</name>
<version>1.1</version>
<version>1.5.1</version>
<inceptionYear>2015</inceptionYear>
<url>https://www.spigotmc.org/resources/fastlogin.14153/</url>
<description>
@ -22,6 +22,7 @@
</properties>
<modules>
<module>core</module>
<module>bukkit</module>
<module>bungee</module>
<module>universal</module>
@ -59,7 +60,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<version>3.0.0</version>
<configuration>
<outputDirectory>${outputDir}</outputDirectory>
</configuration>
@ -83,20 +84,4 @@
</resource>
</resources>
</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.20</version>
</dependency>
</dependencies>
</project>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin</artifactId>
<version>1.1</version>
<version>1.5.1</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -47,6 +47,12 @@
</build>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>fastlogin.core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>fastlogin.bukkit</artifactId>