mirror of
https://github.com/TuxCoding/FastLogin.git
synced 2025-12-24 15:48:07 +01:00
Compare commits
15 Commits
drop-proto
...
premiumUUI
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39be806ae7 | ||
|
|
084afef899 | ||
|
|
8a9eed3a74 | ||
|
|
1ea6d929b1 | ||
|
|
ddc3aa9279 | ||
|
|
2a79a9511b | ||
|
|
791df26702 | ||
|
|
cdf1988f2f | ||
|
|
f476c091bb | ||
|
|
352c72df64 | ||
|
|
2cd0b194aa | ||
|
|
f2e42019d6 | ||
|
|
82ec71e8d0 | ||
|
|
6d207d62ba | ||
|
|
889dab3152 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -15,6 +15,7 @@ nb-configuration.xml
|
||||
|
||||
# Maven
|
||||
target/
|
||||
pom.xml.versionsBackup
|
||||
|
||||
# Gradle
|
||||
.gradle
|
||||
|
||||
49
README.md
49
README.md
@@ -3,7 +3,7 @@
|
||||
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:
|
||||
## Features
|
||||
|
||||
* Detect paid accounts from others
|
||||
* Automatically login paid accounts (premium)
|
||||
@@ -20,31 +20,43 @@ So they don't need to enter passwords. This is also called auto login (auto-logi
|
||||
* Good performance by using async non blocking operations
|
||||
* Locale messages
|
||||
* Import the database from similar plugins
|
||||
* Free
|
||||
* Open source
|
||||
|
||||
## Development builds
|
||||
|
||||
Development builds of this project can be acquired at the provided CI (continuous integration) server. It contains the
|
||||
latest changes from the Source-Code in preparation for the following release. This means they could contain new
|
||||
features, bug fixes and other changes since the last release.
|
||||
|
||||
Nevertheless builds are only tested using a small set of automated and a few manual tests. Therefore they **could**
|
||||
contain new bugs and are likely to be less stable than released versions.
|
||||
|
||||
https://ci.codemc.org/job/Games647/job/FastLogin/changes
|
||||
|
||||
***
|
||||
|
||||
### Commands:
|
||||
## Commands
|
||||
|
||||
/premium [player] Label the invoker or the argument as paid account
|
||||
/cracked [player] Label the invoker or the argument as cracked account
|
||||
|
||||
### Permissions:
|
||||
## Permissions
|
||||
|
||||
fastlogin.bukkit.command.premium
|
||||
fastlogin.bukkit.command.cracked
|
||||
fastlogin.command.premium.other
|
||||
fastlogin.command.cracked.other
|
||||
fastlogin.command.import
|
||||
|
||||
### Requirements:
|
||||
* Plugin: [ProtocolLib](https://www.spigotmc.org/resources/protocollib.1997/) or
|
||||
[ProtocolSupport](https://www.spigotmc.org/resources/protocolsupport.7201/)
|
||||
* [Spigot](https://www.spigotmc.org) 1.7+
|
||||
## Requirements
|
||||
|
||||
* Plugin:
|
||||
* [ProtocolLib](https://www.spigotmc.org/resources/protocollib.1997/) or
|
||||
* [ProtocolSupport](https://www.spigotmc.org/resources/protocolsupport.7201/)
|
||||
* [Spigot](https://www.spigotmc.org) 1.7.10+
|
||||
* Java 8+
|
||||
* Run Spigot and/or BungeeCord/Waterfall in offline mode (see server.properties or config.yml)
|
||||
* An auth plugin. Supported plugins
|
||||
|
||||
#### Bukkit/Spigot/Paper
|
||||
### Bukkit/Spigot/Paper
|
||||
|
||||
* [AuthMe (5.X)](https://dev.bukkit.org/bukkit-plugins/authme-reloaded/)
|
||||
* [xAuth](https://dev.bukkit.org/bukkit-plugins/xauth/)
|
||||
@@ -54,28 +66,29 @@ So they don't need to enter passwords. This is also called auto login (auto-logi
|
||||
* [LoginSecurity](https://dev.bukkit.org/bukkit-plugins/loginsecurity/)
|
||||
* [UltraAuth](https://dev.bukkit.org/bukkit-plugins/ultraauth-aa/)
|
||||
|
||||
#### BungeeCord/Waterfall
|
||||
### BungeeCord/Waterfall
|
||||
|
||||
* [BungeeAuth](https://www.spigotmc.org/resources/bungeeauth.493/)
|
||||
|
||||
***
|
||||
|
||||
### How to install
|
||||
## How to install
|
||||
|
||||
#### Bukkit/Spigot/Paper
|
||||
### Bukkit/Spigot/Paper
|
||||
|
||||
1. Download and install ProtocolLib
|
||||
2. Download and install FastLogin
|
||||
1. Download and install ProtocolLib/ProtocolSupport
|
||||
2. Download and install FastLogin (or FastLoginBukkit for newer versions)
|
||||
3. Set your server in offline mode by setting the value onlinemode in your server.properties to false
|
||||
|
||||
#### BungeeCord/Waterfall
|
||||
### 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 (on the servers where your login plugin is)
|
||||
5. Download and Install FastLogin (or FastLoginBungee in newer versions) on BungeeCord AND Spigot
|
||||
(on the servers where your login plugin is or where player should be able to execute the commands of FastLogin)
|
||||
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. You should *always* firewall your Spigot server that it's only accessible through BungeeCord
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.github.games647</groupId>
|
||||
<artifactId>fastlogin</artifactId>
|
||||
<version>1.11</version>
|
||||
<version>1.11-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<!--Common plugin component-->
|
||||
<dependency>
|
||||
<groupId>com.github.games647</groupId>
|
||||
<artifactId>fastlogin.core</artifactId>
|
||||
@@ -116,6 +117,7 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!--Changing onlinemode on login process-->
|
||||
<dependency>
|
||||
<groupId>com.github.ProtocolSupport</groupId>
|
||||
<artifactId>ProtocolSupport</artifactId>
|
||||
@@ -124,10 +126,11 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!--Provide premium placeholders-->
|
||||
<dependency>
|
||||
<groupId>me.clip</groupId>
|
||||
<artifactId>placeholderapi</artifactId>
|
||||
<version>2.8.4</version>
|
||||
<version>2.8.5</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
<exclusions>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package com.github.games647.fastlogin.bukkit;
|
||||
|
||||
import com.github.games647.craftapi.model.skin.SkinProperty;
|
||||
import com.github.games647.craftapi.model.skin.Property;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
|
||||
/**
|
||||
* Represents a client connecting to the server.
|
||||
*
|
||||
@@ -13,27 +15,46 @@ import java.util.Optional;
|
||||
*/
|
||||
public class BukkitLoginSession extends LoginSession {
|
||||
|
||||
private SkinProperty skinProperty;
|
||||
private final String serverId;
|
||||
private final byte[] verifyToken;
|
||||
|
||||
private boolean verified;
|
||||
|
||||
public BukkitLoginSession(String username, boolean registered, StoredProfile profile) {
|
||||
private Property skinProperty;
|
||||
|
||||
public BukkitLoginSession(String username, String serverId, byte[] verifyToken, boolean registered
|
||||
, StoredProfile profile) {
|
||||
super(username, registered, profile);
|
||||
|
||||
this.serverId = serverId;
|
||||
this.verifyToken = ArrayUtils.clone(verifyToken);
|
||||
}
|
||||
|
||||
//available for BungeeCord
|
||||
public BukkitLoginSession(String username, boolean registered) {
|
||||
this(username, registered, null);
|
||||
this(username, "", ArrayUtils.EMPTY_BYTE_ARRAY, registered, null);
|
||||
}
|
||||
|
||||
//cracked player
|
||||
public BukkitLoginSession(String username, StoredProfile profile) {
|
||||
this(username, false, profile);
|
||||
this(username, "", ArrayUtils.EMPTY_BYTE_ARRAY, false, profile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the verify token the server sent to the client.
|
||||
*
|
||||
* Empty if it's a BungeeCord connection
|
||||
*
|
||||
* @return the verify token from the server
|
||||
*/
|
||||
public byte[] getVerifyToken() {
|
||||
return ArrayUtils.clone(verifyToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return premium skin if available
|
||||
*/
|
||||
public synchronized Optional<SkinProperty> getSkin() {
|
||||
public synchronized Optional<Property> getSkin() {
|
||||
return Optional.ofNullable(skinProperty);
|
||||
}
|
||||
|
||||
@@ -41,7 +62,7 @@ public class BukkitLoginSession extends LoginSession {
|
||||
* Sets the premium skin property which was retrieved by the session server
|
||||
* @param skinProperty premium skin
|
||||
*/
|
||||
public synchronized void setSkinProperty(SkinProperty skinProperty) {
|
||||
public synchronized void setSkinProperty(Property skinProperty) {
|
||||
this.skinProperty = skinProperty;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.github.games647.fastlogin.bukkit;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
/**
|
||||
* Encryption and decryption minecraft util for connection between servers
|
||||
* and paid Minecraft account clients.
|
||||
*
|
||||
* @see net.minecraft.server.MinecraftEncryption
|
||||
*/
|
||||
public class EncryptionUtil {
|
||||
|
||||
public static final int VERIFY_TOKEN_LENGTH = 4;
|
||||
public static final String KEY_PAIR_ALGORITHM = "RSA";
|
||||
|
||||
private EncryptionUtil() {
|
||||
//utility
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a RSA key pair
|
||||
*
|
||||
* @return The RSA key pair.
|
||||
*/
|
||||
public static KeyPair generateKeyPair() {
|
||||
try {
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_PAIR_ALGORITHM);
|
||||
|
||||
keyPairGenerator.initialize(1_024);
|
||||
return keyPairGenerator.generateKeyPair();
|
||||
} catch (NoSuchAlgorithmException nosuchalgorithmexception) {
|
||||
//Should be existing in every vm
|
||||
throw new ExceptionInInitializerError(nosuchalgorithmexception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random token. This is used to verify that we are communicating with the same player
|
||||
* in a login session.
|
||||
*
|
||||
* @param random random generator
|
||||
* @return an error with 4 bytes long
|
||||
*/
|
||||
public static byte[] generateVerifyToken(Random random) {
|
||||
byte[] token = new byte[VERIFY_TOKEN_LENGTH];
|
||||
random.nextBytes(token);
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the server id based on client and server data.
|
||||
*
|
||||
* @param sessionId session for the current login attempt
|
||||
* @param sharedSecret shared secret between the client and the server
|
||||
* @param publicKey public key of the server
|
||||
* @return the server id formatted as a hexadecimal string.
|
||||
*/
|
||||
public static String getServerIdHashString(String sessionId, Key sharedSecret, PublicKey publicKey) {
|
||||
try {
|
||||
byte[] serverHash = getServerIdHash(sessionId, sharedSecret, publicKey);
|
||||
return (new BigInteger(serverHash)).toString(16);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts the content and extracts the key spec.
|
||||
*
|
||||
* @param cipher decryption cipher
|
||||
* @param privateKey private key of the server
|
||||
* @param sharedKey the encrypted shared key
|
||||
* @return shared secret key
|
||||
* @throws GeneralSecurityException
|
||||
*/
|
||||
public static SecretKey decryptSharedKey(Cipher cipher, PrivateKey privateKey, byte[] sharedKey)
|
||||
throws GeneralSecurityException {
|
||||
return new SecretKeySpec(decrypt(cipher, privateKey, sharedKey), "AES");
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypted the given data using the cipher.
|
||||
*
|
||||
* @param cipher decryption cypher
|
||||
* @param key server private key
|
||||
* @param data the encrypted data
|
||||
* @return clear text data
|
||||
* @throws GeneralSecurityException if it fails to initialize and decrypt the data
|
||||
*/
|
||||
public static byte[] decrypt(Cipher cipher, PrivateKey key, byte[] data) throws GeneralSecurityException {
|
||||
cipher.init(Cipher.DECRYPT_MODE, key);
|
||||
return cipher.doFinal(data);
|
||||
}
|
||||
|
||||
private static byte[] getServerIdHash(String sessionId, Key sharedSecret, PublicKey publicKey)
|
||||
throws NoSuchAlgorithmException {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
||||
|
||||
digest.update(sessionId.getBytes(StandardCharsets.ISO_8859_1));
|
||||
digest.update(sharedSecret.getEncoded());
|
||||
digest.update(publicKey.getEncoded());
|
||||
|
||||
return digest.digest();
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,16 @@
|
||||
package com.github.games647.fastlogin.bukkit;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.commands.CrackedCommand;
|
||||
import com.github.games647.fastlogin.bukkit.commands.PremiumCommand;
|
||||
import com.github.games647.fastlogin.bukkit.command.CrackedCommand;
|
||||
import com.github.games647.fastlogin.bukkit.command.PremiumCommand;
|
||||
import com.github.games647.fastlogin.bukkit.listener.BungeeListener;
|
||||
import com.github.games647.fastlogin.bukkit.listener.ConnectionListener;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.ProtocolLibListener;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.SkinApplyListener;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocolsupport.ProtocolSupportListener;
|
||||
import com.github.games647.fastlogin.bukkit.tasks.DelayedAuthHook;
|
||||
import com.github.games647.fastlogin.bukkit.task.DelayedAuthHook;
|
||||
import com.github.games647.fastlogin.core.CommonUtil;
|
||||
import com.github.games647.fastlogin.core.PremiumStatus;
|
||||
import com.github.games647.fastlogin.core.messages.ChannelMessage;
|
||||
import com.github.games647.fastlogin.core.message.ChannelMessage;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.PlatformPlugin;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
@@ -33,7 +35,7 @@ import org.slf4j.Logger;
|
||||
public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<CommandSender> {
|
||||
|
||||
//1 minutes should be enough as a timeout for bad internet connection (Server, Client and Mojang)
|
||||
private final ConcurrentMap<String, BukkitLoginSession> loginSession = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<String, BukkitLoginSession> loginSession = CommonUtil.buildCache(1, -1);
|
||||
private final Logger logger = CommonUtil.createLoggerFromJDK(getLogger());
|
||||
private final Map<UUID, PremiumStatus> premiumPlayers = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -62,6 +64,8 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
|
||||
PluginManager pluginManager = getServer().getPluginManager();
|
||||
if (bungeeCord) {
|
||||
setServerStarted();
|
||||
|
||||
//check for incoming messages from the bungeecord version of this plugin
|
||||
getServer().getMessenger().registerIncomingPluginChannel(this, getName(), new BungeeListener(this));
|
||||
getServer().getMessenger().registerOutgoingPluginChannel(this, getName());
|
||||
@@ -73,8 +77,11 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
|
||||
if (pluginManager.isPluginEnabled("ProtocolSupport")) {
|
||||
pluginManager.registerEvents(new ProtocolSupportListener(this), this);
|
||||
} else if (pluginManager.isPluginEnabled("ProtocolLib")) {
|
||||
ProtocolLibListener.register(this);
|
||||
pluginManager.registerEvents(new SkinApplyListener(this), this);
|
||||
} else {
|
||||
logger.warn("ProtocolSupport have to be installed if you don't use BungeeCord");
|
||||
logger.warn("Either ProtocolLib or ProtocolSupport have to be installed if you don't use BungeeCord");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +148,22 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
return premiumPlayers.getOrDefault(onlinePlayer, PremiumStatus.UNKNOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait before the server is fully started. This is workaround, because connections right on startup are not
|
||||
* injected by ProtocolLib
|
||||
*
|
||||
* @return true if ProtocolLib can now intercept packets
|
||||
*/
|
||||
public boolean isServerFullyStarted() {
|
||||
return serverStarted;
|
||||
}
|
||||
|
||||
public void setServerStarted() {
|
||||
if (!this.serverStarted) {
|
||||
this.serverStarted = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPluginMessage(PluginMessageRecipient player, ChannelMessage message) {
|
||||
if (player != null) {
|
||||
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package com.github.games647.fastlogin.bukkit;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import me.clip.placeholderapi.PlaceholderHook;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class PremiumPlaceholder extends PlaceholderHook {
|
||||
public class PremiumPlaceholder extends PlaceholderExpansion {
|
||||
|
||||
private static final String PLACEHOLDER_VARIABLE = "fastlogin_status";
|
||||
|
||||
private final FastLoginBukkit plugin;
|
||||
|
||||
@@ -13,16 +17,37 @@ public class PremiumPlaceholder extends PlaceholderHook {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public static void register(FastLoginBukkit plugin) {
|
||||
PremiumPlaceholder placeholderHook = new PremiumPlaceholder(plugin);
|
||||
PlaceholderAPI.registerPlaceholderHook(PLACEHOLDER_VARIABLE, placeholderHook);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onPlaceholderRequest(Player player, String variable) {
|
||||
if (player != null && "fastlogin_status".contains(variable)) {
|
||||
if (player != null && PLACEHOLDER_VARIABLE.equals(variable)) {
|
||||
return plugin.getStatus(player.getUniqueId()).name();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
public static void register(FastLoginBukkit plugin) {
|
||||
PlaceholderAPI.registerPlaceholderHook(plugin, new PremiumPlaceholder(plugin));
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return PLACEHOLDER_VARIABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlugin() {
|
||||
return plugin.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthor() {
|
||||
return plugin.getDescription().getAuthors().stream().collect(Collectors.joining(", "));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return plugin.getName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.games647.fastlogin.bukkit.commands;
|
||||
package com.github.games647.fastlogin.bukkit.command;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.games647.fastlogin.bukkit.commands;
|
||||
package com.github.games647.fastlogin.bukkit.command;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.github.games647.fastlogin.bukkit.commands;
|
||||
package com.github.games647.fastlogin.bukkit.command;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.messages.ChangePremiumMessage;
|
||||
import com.github.games647.fastlogin.core.messages.ChannelMessage;
|
||||
import com.github.games647.fastlogin.core.message.ChangePremiumMessage;
|
||||
import com.github.games647.fastlogin.core.message.ChannelMessage;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.games647.fastlogin.bukkit.hooks;
|
||||
package com.github.games647.fastlogin.bukkit.hook;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
@@ -42,11 +42,11 @@ public class AuthMeHook implements AuthPlugin<Player>, Listener {
|
||||
|
||||
@Override
|
||||
public boolean forceLogin(Player player) {
|
||||
//skips registration and login
|
||||
if (AuthMeApi.getInstance().isAuthenticated(player)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//skips registration and login
|
||||
AuthMeApi.getInstance().forceLogin(player);
|
||||
return true;
|
||||
}
|
||||
@@ -58,7 +58,7 @@ public class AuthMeHook implements AuthPlugin<Player>, Listener {
|
||||
|
||||
@Override
|
||||
public boolean forceRegister(Player player, String password) {
|
||||
//this automatically registers the player too
|
||||
//this automatically login the player too
|
||||
AuthMeApi.getInstance().forceRegister(player, password);
|
||||
return true;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.games647.fastlogin.bukkit.hooks;
|
||||
package com.github.games647.fastlogin.bukkit.hook;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.games647.fastlogin.bukkit.hooks;
|
||||
package com.github.games647.fastlogin.bukkit.hook;
|
||||
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.games647.fastlogin.bukkit.hooks;
|
||||
package com.github.games647.fastlogin.bukkit.hook;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.games647.fastlogin.bukkit.hooks;
|
||||
package com.github.games647.fastlogin.bukkit.hook;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.games647.fastlogin.bukkit.hooks;
|
||||
package com.github.games647.fastlogin.bukkit.hook;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
@@ -2,11 +2,11 @@ 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.tasks.ForceLoginTask;
|
||||
import com.github.games647.fastlogin.bukkit.task.ForceLoginTask;
|
||||
import com.github.games647.fastlogin.core.PremiumStatus;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
import com.github.games647.fastlogin.core.messages.LoginActionMessage;
|
||||
import com.github.games647.fastlogin.core.messages.LoginActionMessage.Type;
|
||||
import com.github.games647.fastlogin.core.message.LoginActionMessage;
|
||||
import com.github.games647.fastlogin.core.message.LoginActionMessage.Type;
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
|
||||
@@ -70,6 +70,8 @@ public class BungeeListener implements PluginMessageListener {
|
||||
|
||||
//fail if target player is blacklisted because already authenticated or wrong bungeecord id
|
||||
if (checkedPlayer.hasMetadata(plugin.getName())) {
|
||||
plugin.getLog().warn("Received message {} from a blacklisted player {}", loginMessage, checkedPlayer);
|
||||
} else {
|
||||
//fail if BungeeCord support is disabled (id = null)
|
||||
UUID sourceId = loginMessage.getProxyId();
|
||||
if (proxyIds.contains(sourceId)) {
|
||||
@@ -77,8 +79,6 @@ public class BungeeListener implements PluginMessageListener {
|
||||
} else {
|
||||
plugin.getLog().warn("Received proxy id: {} that doesn't exist in the proxy whitelist file", sourceId);
|
||||
}
|
||||
} else {
|
||||
plugin.getLog().warn("Received message {} from a blacklisted player {}", loginMessage, checkedPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,16 +89,12 @@ public class BungeeListener implements PluginMessageListener {
|
||||
InetSocketAddress address = player.getAddress();
|
||||
String id = '/' + address.getAddress().getHostAddress() + ':' + address.getPort();
|
||||
if (type == Type.LOGIN) {
|
||||
plugin.getPremiumPlayers().put(player.getUniqueId(), PremiumStatus.PREMIUM);
|
||||
|
||||
BukkitLoginSession playerSession = new BukkitLoginSession(playerName, true);
|
||||
playerSession.setVerified(true);
|
||||
plugin.getLoginSessions().put(id, playerSession);
|
||||
|
||||
Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, new ForceLoginTask(plugin.getCore(), player), 20L);
|
||||
Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, new ForceLoginTask(plugin.getCore(), player), 10L);
|
||||
} else if (type == Type.REGISTER) {
|
||||
plugin.getPremiumPlayers().put(player.getUniqueId(), PremiumStatus.PREMIUM);
|
||||
|
||||
Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, () -> {
|
||||
AuthPlugin<Player> authPlugin = plugin.getCore().getAuthPluginHook();
|
||||
try {
|
||||
@@ -112,8 +108,9 @@ public class BungeeListener implements PluginMessageListener {
|
||||
} catch (Exception ex) {
|
||||
plugin.getLog().error("Failed to query isRegistered for player: {}", player, ex);
|
||||
}
|
||||
}, 20L);
|
||||
}, 10L);
|
||||
} else if (type == Type.CRACKED) {
|
||||
//we don't start a forcelogin task here so update it manually
|
||||
plugin.getPremiumPlayers().put(player.getUniqueId(), PremiumStatus.CRACKED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package com.github.games647.fastlogin.bukkit.listener;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.bukkit.tasks.ForceLoginTask;
|
||||
import com.github.games647.fastlogin.bukkit.task.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.PlayerLoginEvent;
|
||||
import org.bukkit.event.player.PlayerLoginEvent.Result;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
/**
|
||||
@@ -24,6 +27,13 @@ public class ConnectionListener 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, plugin.getCore().getMessage("not-started"));
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onPlayerJoin(PlayerJoinEvent joinEvent) {
|
||||
Player player = joinEvent.getPlayer();
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
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.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.JoinManagement;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class NameCheckTask extends JoinManagement<Player, CommandSender, ProtocolLibLoginSource>
|
||||
implements Runnable {
|
||||
|
||||
private final FastLoginBukkit plugin;
|
||||
private final PacketEvent packetEvent;
|
||||
private final PublicKey publicKey;
|
||||
|
||||
private final Random random;
|
||||
|
||||
private final Player player;
|
||||
private final String username;
|
||||
|
||||
public NameCheckTask(FastLoginBukkit plugin, PacketEvent packetEvent, Random random,
|
||||
Player player, String username, PublicKey publicKey) {
|
||||
super(plugin.getCore(), plugin.getCore().getAuthPluginHook());
|
||||
|
||||
this.plugin = plugin;
|
||||
this.packetEvent = packetEvent;
|
||||
this.publicKey = publicKey;
|
||||
this.random = random;
|
||||
this.player = player;
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
super.onLogin(username, new ProtocolLibLoginSource(packetEvent, player, random, publicKey));
|
||||
} finally {
|
||||
ProtocolLibrary.getProtocolManager().getAsynchronousManager().signalPacketTransmission(packetEvent);
|
||||
}
|
||||
}
|
||||
|
||||
//Minecraft server implementation
|
||||
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L161
|
||||
@Override
|
||||
public void requestPremiumLogin(ProtocolLibLoginSource source, StoredProfile profile
|
||||
, String username, boolean registered) {
|
||||
try {
|
||||
source.setOnlineMode();
|
||||
} catch (Exception ex) {
|
||||
plugin.getLog().error("Cannot send encryption packet. Falling back to cracked login for: {}", profile, ex);
|
||||
return;
|
||||
}
|
||||
|
||||
String ip = player.getAddress().getAddress().getHostAddress();
|
||||
core.getPendingLogin().put(ip + username, new Object());
|
||||
|
||||
String serverId = source.getServerId();
|
||||
byte[] verify = source.getVerifyToken();
|
||||
|
||||
BukkitLoginSession playerSession = new BukkitLoginSession(username, serverId, verify, registered, profile);
|
||||
plugin.getLoginSessions().put(player.getAddress().toString(), playerSession);
|
||||
//cancel only if the player has a paid account otherwise login as normal offline player
|
||||
synchronized (packetEvent.getAsyncMarker().getProcessingLock()) {
|
||||
packetEvent.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startCrackedSession(ProtocolLibLoginSource source, StoredProfile profile, String username) {
|
||||
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
|
||||
plugin.getLoginSessions().put(player.getAddress().toString(), loginSession);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.github.games647.fastlogin.bukkit.EncryptionUtil;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import static com.comphenix.protocol.PacketType.Login.Client.ENCRYPTION_BEGIN;
|
||||
import static com.comphenix.protocol.PacketType.Login.Client.START;
|
||||
|
||||
public class ProtocolLibListener extends PacketAdapter {
|
||||
|
||||
private static final int WORKER_THREADS = 3;
|
||||
|
||||
private final FastLoginBukkit plugin;
|
||||
|
||||
//just create a new once on plugin enable. This used for verify token generation
|
||||
private final SecureRandom random = new SecureRandom();
|
||||
private final KeyPair keyPair = EncryptionUtil.generateKeyPair();
|
||||
|
||||
public ProtocolLibListener(FastLoginBukkit plugin) {
|
||||
//run async in order to not block the server, because we are making api calls to Mojang
|
||||
super(params()
|
||||
.plugin(plugin)
|
||||
.types(START, ENCRYPTION_BEGIN)
|
||||
.optionAsync());
|
||||
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public static void register(FastLoginBukkit plugin) {
|
||||
//they will be created with a static builder, because otherwise it will throw a NoClassDefFoundError
|
||||
ProtocolLibrary.getProtocolManager()
|
||||
.getAsynchronousManager()
|
||||
.registerAsyncHandler(new ProtocolLibListener(plugin))
|
||||
.start(WORKER_THREADS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent packetEvent) {
|
||||
if (packetEvent.isCancelled()
|
||||
|| plugin.getCore().getAuthPluginHook()== null
|
||||
|| !plugin.isServerFullyStarted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player sender = packetEvent.getPlayer();
|
||||
PacketType packetType = packetEvent.getPacketType();
|
||||
if (packetType == START) {
|
||||
onLogin(packetEvent, sender);
|
||||
} else {
|
||||
onEncryptionBegin(packetEvent, sender);
|
||||
}
|
||||
}
|
||||
|
||||
private void onEncryptionBegin(PacketEvent packetEvent, Player sender) {
|
||||
byte[] sharedSecret = packetEvent.getPacket().getByteArrays().read(0);
|
||||
|
||||
packetEvent.getAsyncMarker().incrementProcessingDelay();
|
||||
Runnable verifyTask = new VerifyResponseTask(plugin, packetEvent, sender, sharedSecret, keyPair);
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, verifyTask);
|
||||
}
|
||||
|
||||
private void onLogin(PacketEvent packetEvent, Player player) {
|
||||
//this includes ip:port. Should be unique for an incoming login request with a timeout of 2 minutes
|
||||
String sessionKey = player.getAddress().toString();
|
||||
|
||||
//remove old data every time on a new login in order to keep the session only for one person
|
||||
plugin.getLoginSessions().remove(sessionKey);
|
||||
|
||||
//player.getName() won't work at this state
|
||||
PacketContainer packet = packetEvent.getPacket();
|
||||
|
||||
String username = packet.getGameProfiles().read(0).getName();
|
||||
plugin.getLog().trace("GameProfile {} with {} connecting", sessionKey, username);
|
||||
|
||||
packetEvent.getAsyncMarker().incrementProcessingDelay();
|
||||
Runnable nameCheckTask = new NameCheckTask(plugin, packetEvent, random, player, username, keyPair.getPublic());
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, nameCheckTask);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||
import com.github.games647.fastlogin.bukkit.EncryptionUtil;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSource;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import static com.comphenix.protocol.PacketType.Login.Server.DISCONNECT;
|
||||
import static com.comphenix.protocol.PacketType.Login.Server.ENCRYPTION_BEGIN;
|
||||
|
||||
public class ProtocolLibLoginSource implements LoginSource {
|
||||
|
||||
private final PacketEvent packetEvent;
|
||||
private final Player player;
|
||||
|
||||
private final Random random;
|
||||
private final PublicKey publicKey;
|
||||
|
||||
private final String serverId = "";
|
||||
private byte[] verifyToken;
|
||||
|
||||
public ProtocolLibLoginSource(PacketEvent packetEvent, Player player, Random random, PublicKey publicKey) {
|
||||
this.packetEvent = packetEvent;
|
||||
this.player = player;
|
||||
this.random = random;
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnlineMode() throws Exception {
|
||||
verifyToken = EncryptionUtil.generateVerifyToken(random);
|
||||
|
||||
/*
|
||||
* Packet Information: http://wiki.vg/Protocol#Encryption_Request
|
||||
*
|
||||
* ServerID="" (String) key=public server key verifyToken=random 4 byte array
|
||||
*/
|
||||
PacketContainer newPacket = new PacketContainer(ENCRYPTION_BEGIN);
|
||||
|
||||
newPacket.getStrings().write(0, serverId);
|
||||
newPacket.getSpecificModifier(PublicKey.class).write(0, publicKey);
|
||||
|
||||
newPacket.getByteArrays().write(0, verifyToken);
|
||||
|
||||
//serverId is a empty string
|
||||
ProtocolLibrary.getProtocolManager().sendServerPacket(player, newPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kick(String message) throws InvocationTargetException {
|
||||
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
|
||||
|
||||
PacketContainer kickPacket = new PacketContainer(DISCONNECT);
|
||||
kickPacket.getChatComponents().write(0, WrappedChatComponent.fromText(message));
|
||||
|
||||
try {
|
||||
//send kick packet at login state
|
||||
//the normal event.getPlayer.kickPlayer(String) method does only work at play state
|
||||
protocolManager.sendServerPacket(player, kickPacket);
|
||||
} finally {
|
||||
//tell the server that we want to close the connection
|
||||
player.kickPlayer("Disconnect");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getAddress() {
|
||||
return packetEvent.getPlayer().getAddress();
|
||||
}
|
||||
|
||||
public String getServerId() {
|
||||
return serverId;
|
||||
}
|
||||
|
||||
public byte[] getVerifyToken() {
|
||||
return ArrayUtils.clone(verifyToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + '{' +
|
||||
"packetEvent=" + packetEvent +
|
||||
", player=" + player +
|
||||
", random=" + random +
|
||||
", serverId='" + serverId + '\'' +
|
||||
", verifyToken=" + Arrays.toString(verifyToken) +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
||||
|
||||
import com.comphenix.protocol.reflect.MethodUtils;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||
import com.comphenix.protocol.wrappers.WrappedSignedProperty;
|
||||
import com.github.games647.craftapi.model.skin.Property;
|
||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
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.PlayerLoginEvent;
|
||||
import org.bukkit.event.player.PlayerLoginEvent.Result;
|
||||
|
||||
public class SkinApplyListener implements Listener {
|
||||
|
||||
private static final Class<?> GAME_PROFILE = MinecraftReflection.getGameProfileClass();
|
||||
private static final MethodAccessor GET_PROPERTIES = Accessors.getMethodAccessor(GAME_PROFILE, "getProperties");
|
||||
|
||||
private final FastLoginBukkit plugin;
|
||||
|
||||
public SkinApplyListener(FastLoginBukkit plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
//run this on the loginEvent to let skins plugins see the skin like in normal Minecraft behaviour
|
||||
public void onPlayerLogin(PlayerLoginEvent loginEvent) {
|
||||
if (loginEvent.getResult() != Result.ALLOWED) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = loginEvent.getPlayer();
|
||||
|
||||
if (plugin.getConfig().getBoolean("forwardSkin")) {
|
||||
//go through every session, because player.getAddress is null
|
||||
//loginEvent.getAddress is just a InetAddress not InetSocketAddress, so not unique enough
|
||||
for (BukkitLoginSession session : plugin.getLoginSessions().values()) {
|
||||
if (session.getUsername().equals(player.getName())) {
|
||||
session.getSkin().ifPresent(skin -> applySkin(player, skin.getValue(), skin.getSignature()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void applySkin(Player player, String skinData, String signature) {
|
||||
WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(player);
|
||||
|
||||
WrappedSignedProperty skin = WrappedSignedProperty.fromValues(Property.TEXTURE_KEY, skinData, signature);
|
||||
try {
|
||||
gameProfile.getProperties().put(Property.TEXTURE_KEY, skin);
|
||||
} catch (ClassCastException castException) {
|
||||
//Cauldron, MCPC, Thermos, ...
|
||||
Object map = GET_PROPERTIES.invoke(gameProfile.getHandle());
|
||||
try {
|
||||
MethodUtils.invokeMethod(map, "put", new Object[]{Property.TEXTURE_KEY, skin.getHandle()});
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
|
||||
plugin.getLog().error("Error setting premium skin of: {}", player, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||
import com.github.games647.craftapi.model.auth.Verification;
|
||||
import com.github.games647.craftapi.model.skin.Property;
|
||||
import com.github.games647.craftapi.resolver.MojangResolver;
|
||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
||||
import com.github.games647.fastlogin.bukkit.EncryptionUtil;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import static com.comphenix.protocol.PacketType.Login.Client.START;
|
||||
import static com.comphenix.protocol.PacketType.Login.Server.DISCONNECT;
|
||||
|
||||
public class VerifyResponseTask implements Runnable {
|
||||
|
||||
private final FastLoginBukkit plugin;
|
||||
private final PacketEvent packetEvent;
|
||||
private final KeyPair serverKey;
|
||||
|
||||
private final Player player;
|
||||
|
||||
private final byte[] sharedSecret;
|
||||
|
||||
public VerifyResponseTask(FastLoginBukkit plugin, PacketEvent packetEvent, Player player,
|
||||
byte[] sharedSecret, KeyPair keyPair) {
|
||||
this.plugin = plugin;
|
||||
this.packetEvent = packetEvent;
|
||||
this.player = player;
|
||||
this.sharedSecret = Arrays.copyOf(sharedSecret, sharedSecret.length);
|
||||
this.serverKey = keyPair;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
BukkitLoginSession session = plugin.getLoginSessions().get(player.getAddress().toString());
|
||||
if (session == null) {
|
||||
disconnect(plugin.getCore().getMessage("invalid-request"), true
|
||||
, "GameProfile {0} tried to send encryption response at invalid state", player.getAddress());
|
||||
} else {
|
||||
verifyResponse(session);
|
||||
}
|
||||
} finally {
|
||||
//this is a fake packet; it shouldn't be send to the server
|
||||
synchronized (packetEvent.getAsyncMarker().getProcessingLock()) {
|
||||
packetEvent.setCancelled(true);
|
||||
}
|
||||
|
||||
ProtocolLibrary.getProtocolManager().getAsynchronousManager().signalPacketTransmission(packetEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyResponse(BukkitLoginSession session) {
|
||||
PrivateKey privateKey = serverKey.getPrivate();
|
||||
|
||||
Cipher cipher;
|
||||
SecretKey loginKey;
|
||||
try {
|
||||
cipher = Cipher.getInstance(privateKey.getAlgorithm());
|
||||
|
||||
loginKey = EncryptionUtil.decryptSharedKey(cipher, privateKey, sharedSecret);
|
||||
} catch (GeneralSecurityException securityEx) {
|
||||
disconnect("error-kick", false, "Cannot decrypt received contents", securityEx);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!checkVerifyToken(session, cipher, privateKey) || !encryptConnection(loginKey)) {
|
||||
return;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
disconnect("error-kick", false, "Cannot decrypt received contents", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
String serverId = EncryptionUtil.getServerIdHashString("", loginKey, serverKey.getPublic());
|
||||
|
||||
String username = session.getUsername();
|
||||
InetSocketAddress socketAddress = player.getAddress();
|
||||
try {
|
||||
MojangResolver resolver = plugin.getCore().getResolver();
|
||||
InetAddress address = socketAddress.getAddress();
|
||||
Optional<Verification> response = resolver.hasJoined(username, serverId, address);
|
||||
if (response.isPresent()) {
|
||||
Verification verification = response.get();
|
||||
UUID id = verification.getId();
|
||||
|
||||
plugin.getLog().info("GameProfile {} with {} has a verified premium account", username, id);
|
||||
|
||||
Property[] properties = verification.getProperties();
|
||||
if (properties.length > 0) {
|
||||
session.setSkinProperty(properties[0]);
|
||||
}
|
||||
|
||||
session.setUuid(id);
|
||||
session.setVerified(true);
|
||||
|
||||
setPremiumUUID(session.getUuid());
|
||||
receiveFakeStartPacket(username);
|
||||
} else {
|
||||
//user tried to fake a authentication
|
||||
disconnect(plugin.getCore().getMessage("invalid-session"), true
|
||||
, "GameProfile {0} ({1}) tried to log in with an invalid session ServerId: {2}"
|
||||
, session.getUsername(), socketAddress, serverId);
|
||||
}
|
||||
} catch (IOException ioEx) {
|
||||
disconnect("error-kick", false, "Failed to connect to session server", ioEx);
|
||||
}
|
||||
}
|
||||
|
||||
private void setPremiumUUID(UUID premiumUUID) {
|
||||
boolean uuidEnabled = plugin.getConfig().getBoolean("premiumUuid");
|
||||
plugin.getLog().info("Setting UUID {} based on config: {}", premiumUUID, uuidEnabled);
|
||||
try {
|
||||
Object networkManager = getNetworkManager();
|
||||
Field uuidField = FieldUtils.getField(networkManager.getClass(), "spoofedUUID");
|
||||
Object oldValue = uuidField.get(player);
|
||||
|
||||
plugin.getLog().info("spoofed UUID field exits? {} with {}", uuidField, oldValue);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
plugin.getLog().error("Failed to query field of {}", player);
|
||||
}
|
||||
|
||||
if (uuidEnabled && premiumUUID != null) {
|
||||
try {
|
||||
Object networkManager = getNetworkManager();
|
||||
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/NetworkManager.java#L69
|
||||
FieldUtils.writeField(networkManager, "spoofedUUID", premiumUUID, true);
|
||||
} catch (Exception exc) {
|
||||
plugin.getLog().error("Error setting premium uuid of {}", player, exc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkVerifyToken(BukkitLoginSession session, Cipher cipher, PrivateKey privateKey)
|
||||
throws GeneralSecurityException {
|
||||
byte[] requestVerify = session.getVerifyToken();
|
||||
//encrypted verify token
|
||||
byte[] responseVerify = packetEvent.getPacket().getByteArrays().read(1);
|
||||
|
||||
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L182
|
||||
if (!Arrays.equals(requestVerify, EncryptionUtil.decrypt(cipher, privateKey, responseVerify))) {
|
||||
//check if the verify token are equal to the server sent one
|
||||
disconnect(plugin.getCore().getMessage("invalid-verify-token"), true
|
||||
, "GameProfile {0} ({1}) tried to login with an invalid verify token. Server: {2} Client: {3}"
|
||||
, session.getUsername(), packetEvent.getPlayer().getAddress(), requestVerify, responseVerify);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//try to get the networkManager from ProtocolLib
|
||||
private Object getNetworkManager() throws IllegalAccessException, ClassNotFoundException {
|
||||
Object injectorContainer = TemporaryPlayerFactory.getInjectorFromPlayer(player);
|
||||
|
||||
//ChannelInjector
|
||||
Class<?> injectorClass = Class.forName("com.comphenix.protocol.injector.netty.Injector");
|
||||
Object rawInjector = FuzzyReflection.getFieldValue(injectorContainer, injectorClass, true);
|
||||
return FieldUtils.readField(rawInjector, "networkManager", true);
|
||||
}
|
||||
|
||||
private boolean encryptConnection(SecretKey loginKey) throws IllegalArgumentException {
|
||||
try {
|
||||
//get the NMS connection handle of this player
|
||||
Object networkManager = getNetworkManager();
|
||||
|
||||
//try to detect the method by parameters
|
||||
Method encryptMethod = FuzzyReflection
|
||||
.fromObject(networkManager).getMethodByParameters("a", SecretKey.class);
|
||||
|
||||
//encrypt/decrypt following packets
|
||||
//the client expects this behaviour
|
||||
encryptMethod.invoke(networkManager, loginKey);
|
||||
} catch (Exception ex) {
|
||||
disconnect("error-kick", false, "Couldn't enable encryption", ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void disconnect(String kickReason, boolean debug, String logMessage, Object... arguments) {
|
||||
if (debug) {
|
||||
plugin.getLog().debug(logMessage, arguments);
|
||||
} else {
|
||||
plugin.getLog().error(logMessage, arguments);
|
||||
}
|
||||
|
||||
kickPlayer(plugin.getCore().getMessage(kickReason));
|
||||
}
|
||||
|
||||
private void kickPlayer(String reason) {
|
||||
PacketContainer kickPacket = new PacketContainer(DISCONNECT);
|
||||
kickPacket.getChatComponents().write(0, WrappedChatComponent.fromText(reason));
|
||||
try {
|
||||
//send kick packet at login state
|
||||
//the normal event.getPlayer.kickPlayer(String) method does only work at play state
|
||||
ProtocolLibrary.getProtocolManager().sendServerPacket(player, kickPacket);
|
||||
//tell the server that we want to close the connection
|
||||
player.kickPlayer("Disconnect");
|
||||
} catch (InvocationTargetException ex) {
|
||||
plugin.getLog().error("Error sending kick packet for: {}", player, ex);
|
||||
}
|
||||
}
|
||||
|
||||
//fake a new login packet in order to let the server handle all the other stuff
|
||||
private void receiveFakeStartPacket(String username) {
|
||||
//see StartPacketListener for packet information
|
||||
PacketContainer startPacket = new PacketContainer(START);
|
||||
|
||||
//uuid is ignored by the packet definition
|
||||
WrappedGameProfile fakeProfile = new WrappedGameProfile(UUID.randomUUID(), username);
|
||||
startPacket.getGameProfiles().write(0, fakeProfile);
|
||||
try {
|
||||
//we don't want to handle our own packets so ignore filters
|
||||
ProtocolLibrary.getProtocolManager().recieveClientPacket(player, startPacket, false);
|
||||
} catch (InvocationTargetException | IllegalAccessException ex) {
|
||||
plugin.getLog().warn("Failed to fake a new start packet for: {}", username, ex);
|
||||
//cancel the event in order to prevent the server receiving an invalid packet
|
||||
kickPlayer(plugin.getCore().getMessage("error-kick"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.github.games647.fastlogin.bukkit.listener.protocolsupport;
|
||||
|
||||
import com.github.games647.craftapi.model.skin.SkinProperty;
|
||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
@@ -13,8 +12,8 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import protocolsupport.api.events.ConnectionCloseEvent;
|
||||
import protocolsupport.api.events.PlayerLoginFinishEvent;
|
||||
import protocolsupport.api.events.PlayerLoginStartEvent;
|
||||
import protocolsupport.api.events.PlayerPropertiesResolveEvent;
|
||||
|
||||
public class ProtocolSupportListener extends JoinManagement<Player, CommandSender, ProtocolLoginSource>
|
||||
implements Listener {
|
||||
@@ -49,12 +48,15 @@ public class ProtocolSupportListener extends JoinManagement<Player, CommandSende
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPropertiesResolve(PlayerPropertiesResolveEvent propertiesResolveEvent) {
|
||||
InetSocketAddress address = propertiesResolveEvent.getAddress();
|
||||
public void onPropertiesResolve(PlayerLoginFinishEvent loginFinishEvent) {
|
||||
if (!loginFinishEvent.isOnlineMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
InetSocketAddress address = loginFinishEvent.getAddress();
|
||||
BukkitLoginSession session = plugin.getLoginSessions().get(address.toString());
|
||||
|
||||
//skin was resolved -> premium player
|
||||
if (propertiesResolveEvent.hasProperty(SkinProperty.TEXTURE_KEY) && session != null) {
|
||||
if (session != null) {
|
||||
session.setVerified(true);
|
||||
}
|
||||
}
|
||||
@@ -67,7 +69,8 @@ public class ProtocolSupportListener extends JoinManagement<Player, CommandSende
|
||||
String ip = source.getAddress().getAddress().getHostAddress();
|
||||
plugin.getCore().getPendingLogin().put(ip + username, new Object());
|
||||
|
||||
BukkitLoginSession playerSession = new BukkitLoginSession(username, registered, profile);
|
||||
BukkitLoginSession playerSession = new BukkitLoginSession(username, null, null
|
||||
, registered, profile);
|
||||
plugin.getLoginSessions().put(source.getAddress().toString(), playerSession);
|
||||
if (plugin.getConfig().getBoolean("premiumUuid")) {
|
||||
source.getLoginStartEvent().setUseOnlineModeUUID(true);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package com.github.games647.fastlogin.bukkit.tasks;
|
||||
package com.github.games647.fastlogin.bukkit.task;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.bukkit.hooks.AuthMeHook;
|
||||
import com.github.games647.fastlogin.bukkit.hooks.CrazyLoginHook;
|
||||
import com.github.games647.fastlogin.bukkit.hooks.LogItHook;
|
||||
import com.github.games647.fastlogin.bukkit.hooks.LoginSecurityHook;
|
||||
import com.github.games647.fastlogin.bukkit.hooks.UltraAuthHook;
|
||||
import com.github.games647.fastlogin.bukkit.hooks.xAuthHook;
|
||||
import com.github.games647.fastlogin.bukkit.hook.AuthMeHook;
|
||||
import com.github.games647.fastlogin.bukkit.hook.CrazyLoginHook;
|
||||
import com.github.games647.fastlogin.bukkit.hook.LogItHook;
|
||||
import com.github.games647.fastlogin.bukkit.hook.LoginSecurityHook;
|
||||
import com.github.games647.fastlogin.bukkit.hook.UltraAuthHook;
|
||||
import com.github.games647.fastlogin.bukkit.hook.xAuthHook;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
@@ -36,6 +36,10 @@ public class DelayedAuthHook implements Runnable {
|
||||
+ "and BungeeCord is deactivated. "
|
||||
+ "Either one or both of the checks have to pass in order to use this plugin");
|
||||
}
|
||||
|
||||
if (hookFound) {
|
||||
plugin.setServerStarted();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isHookFound() {
|
||||
@@ -1,8 +1,9 @@
|
||||
package com.github.games647.fastlogin.bukkit.tasks;
|
||||
package com.github.games647.fastlogin.bukkit.task;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.messages.SuccessMessage;
|
||||
import com.github.games647.fastlogin.core.PremiumStatus;
|
||||
import com.github.games647.fastlogin.core.message.SuccessMessage;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.ForceLoginManagement;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
||||
@@ -28,11 +29,17 @@ public class ForceLoginTask extends ForceLoginManagement<Player, CommandSender,
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
//blacklist this target player for BungeeCord Id brute force attacks
|
||||
//blacklist this target player for BungeeCord ID brute force attacks
|
||||
FastLoginBukkit plugin = core.getPlugin();
|
||||
player.setMetadata(core.getPlugin().getName(), new FixedMetadataValue(plugin, true));
|
||||
|
||||
super.run();
|
||||
|
||||
if (isOnlineMode()) {
|
||||
plugin.getPremiumPlayers().put(player.getUniqueId(), PremiumStatus.PREMIUM);
|
||||
} else {
|
||||
plugin.getPremiumPlayers().put(player.getUniqueId(), PremiumStatus.CRACKED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.github.games647.fastlogin.bukkit;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class EncryptionUtilTest {
|
||||
|
||||
@Test
|
||||
public void testVerifyToken() throws Exception {
|
||||
SecureRandom random = new SecureRandom();
|
||||
byte[] token = EncryptionUtil.generateVerifyToken(random);
|
||||
|
||||
assertThat(token, notNullValue());
|
||||
assertThat(token.length, is(4));
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void testDecryptSharedSecret() throws Exception {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testDecryptData() throws Exception {
|
||||
//
|
||||
// }
|
||||
|
||||
// private static SecretKey createNewSharedKey() {
|
||||
// try {
|
||||
// KeyGenerator keygenerator = KeyGenerator.getInstance("AES");
|
||||
// keygenerator.init(128);
|
||||
// return keygenerator.generateKey();
|
||||
// } catch (NoSuchAlgorithmException nosuchalgorithmexception) {
|
||||
// throw new Error(nosuchalgorithmexception);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.github.games647</groupId>
|
||||
<artifactId>fastlogin</artifactId>
|
||||
<version>1.11</version>
|
||||
<version>1.11-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<!--Common plugin component-->
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>fastlogin.core</artifactId>
|
||||
@@ -82,6 +83,7 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!--Login plugin-->
|
||||
<dependency>
|
||||
<groupId>me.vik1395</groupId>
|
||||
<artifactId>BungeeAuth</artifactId>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.github.games647.fastlogin.bungee;
|
||||
|
||||
import com.github.games647.fastlogin.bungee.hooks.BungeeAuthHook;
|
||||
import com.github.games647.fastlogin.bungee.hook.BungeeAuthHook;
|
||||
import com.github.games647.fastlogin.bungee.listener.ConnectListener;
|
||||
import com.github.games647.fastlogin.bungee.listener.MessageListener;
|
||||
import com.github.games647.fastlogin.core.CommonUtil;
|
||||
import com.github.games647.fastlogin.core.messages.ChannelMessage;
|
||||
import com.github.games647.fastlogin.core.message.ChannelMessage;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.PlatformPlugin;
|
||||
import com.google.common.collect.MapMaker;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.games647.fastlogin.bungee.hooks;
|
||||
package com.github.games647.fastlogin.bungee.hook;
|
||||
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
|
||||
@@ -2,8 +2,8 @@ package com.github.games647.fastlogin.bungee.listener;
|
||||
|
||||
import com.github.games647.craftapi.UUIDAdapter;
|
||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||
import com.github.games647.fastlogin.bungee.tasks.AsyncPremiumCheck;
|
||||
import com.github.games647.fastlogin.bungee.tasks.ForceLoginTask;
|
||||
import com.github.games647.fastlogin.bungee.task.AsyncPremiumCheck;
|
||||
import com.github.games647.fastlogin.bungee.task.ForceLoginTask;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@ package com.github.games647.fastlogin.bungee.listener;
|
||||
|
||||
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
|
||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||
import com.github.games647.fastlogin.bungee.tasks.AsyncToggleMessage;
|
||||
import com.github.games647.fastlogin.bungee.task.AsyncToggleMessage;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.messages.ChangePremiumMessage;
|
||||
import com.github.games647.fastlogin.core.message.ChangePremiumMessage;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.games647.fastlogin.bungee.tasks;
|
||||
package com.github.games647.fastlogin.bungee.task;
|
||||
|
||||
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
|
||||
import com.github.games647.fastlogin.bungee.BungeeLoginSource;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.games647.fastlogin.bungee.tasks;
|
||||
package com.github.games647.fastlogin.bungee.task;
|
||||
|
||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.github.games647.fastlogin.bungee.tasks;
|
||||
package com.github.games647.fastlogin.bungee.task;
|
||||
|
||||
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
|
||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||
import com.github.games647.fastlogin.core.messages.ChannelMessage;
|
||||
import com.github.games647.fastlogin.core.messages.LoginActionMessage;
|
||||
import com.github.games647.fastlogin.core.messages.LoginActionMessage.Type;
|
||||
import com.github.games647.fastlogin.core.message.ChannelMessage;
|
||||
import com.github.games647.fastlogin.core.message.LoginActionMessage;
|
||||
import com.github.games647.fastlogin.core.message.LoginActionMessage.Type;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.ForceLoginManagement;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
||||
@@ -54,7 +54,6 @@ public class ForceLoginTask
|
||||
@Override
|
||||
public boolean forceRegister(ProxiedPlayer player) {
|
||||
return session.isAlreadyLogged() || super.forceRegister(player);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -62,7 +61,7 @@ public class ForceLoginTask
|
||||
//sub channel name
|
||||
Type type = Type.LOGIN;
|
||||
if (session.needsRegistration()) {
|
||||
type = Type.LOGIN;
|
||||
type = Type.REGISTER;
|
||||
}
|
||||
|
||||
UUID proxyId = UUID.fromString(ProxyServer.getInstance().getConfig().getUuid());
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.github.games647</groupId>
|
||||
<artifactId>fastlogin</artifactId>
|
||||
<version>1.11</version>
|
||||
<version>1.11-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
<version>2.7.8</version>
|
||||
<version>3.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!--Logging framework implements slf4j which is required by hikari-->
|
||||
@@ -63,6 +63,7 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!--Common component for contacting the Mojang API-->
|
||||
<dependency>
|
||||
<groupId>com.github.games647</groupId>
|
||||
<artifactId>craftapi</artifactId>
|
||||
|
||||
@@ -2,9 +2,11 @@ package com.github.games647.fastlogin.core.hooks;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Random;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class DefaultPasswordGenerator<P> implements PasswordGenerator<P> {
|
||||
|
||||
private static final int PASSWORD_LENGTH = 8;
|
||||
private static final char[] PASSWORD_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
.toCharArray();
|
||||
|
||||
@@ -13,9 +15,10 @@ public class DefaultPasswordGenerator<P> implements PasswordGenerator<P> {
|
||||
@Override
|
||||
public String getRandomPassword(P player) {
|
||||
StringBuilder generatedPassword = new StringBuilder(8);
|
||||
for (int i = 1; i <= 8; i++) {
|
||||
generatedPassword.append(PASSWORD_CHARACTERS[random.nextInt(PASSWORD_CHARACTERS.length - 1)]);
|
||||
}
|
||||
IntStream.rangeClosed(1, PASSWORD_LENGTH)
|
||||
.map(i -> random.nextInt(PASSWORD_CHARACTERS.length - 1))
|
||||
.mapToObj(pos -> PASSWORD_CHARACTERS[pos])
|
||||
.forEach(generatedPassword::append);
|
||||
|
||||
return generatedPassword.toString();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.games647.fastlogin.core.messages;
|
||||
package com.github.games647.fastlogin.core.message;
|
||||
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.games647.fastlogin.core.messages;
|
||||
package com.github.games647.fastlogin.core.message;
|
||||
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.games647.fastlogin.core.messages;
|
||||
package com.github.games647.fastlogin.core.message;
|
||||
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.games647.fastlogin.core.messages;
|
||||
package com.github.games647.fastlogin.core.message;
|
||||
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
@@ -11,6 +11,7 @@ import com.google.common.net.HostAndPort;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
@@ -74,7 +75,7 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
|
||||
.forEach((key, message) -> {
|
||||
String colored = CommonUtil.translateColorCodes((String) message);
|
||||
if (!colored.isEmpty()) {
|
||||
localeMessages.put(key, colored);
|
||||
localeMessages.put(key, colored.replace("/newline", "\n"));
|
||||
}
|
||||
});
|
||||
} catch (IOException ioEx) {
|
||||
@@ -103,15 +104,26 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
|
||||
}
|
||||
|
||||
private Configuration loadFile(String fileName) throws IOException {
|
||||
Configuration defaults;
|
||||
|
||||
ConfigurationProvider configProvider = ConfigurationProvider.getProvider(YamlConfiguration.class);
|
||||
|
||||
Configuration defaults;
|
||||
try (InputStream defaultStream = getClass().getClassLoader().getResourceAsStream(fileName)) {
|
||||
defaults = configProvider.load(defaultStream);
|
||||
}
|
||||
|
||||
Path file = plugin.getPluginFolder().resolve(fileName);
|
||||
return configProvider.load(Files.newBufferedReader(file), defaults);
|
||||
|
||||
Configuration config;
|
||||
try (Reader reader = Files.newBufferedReader(file)) {
|
||||
config = configProvider.load(reader, defaults);
|
||||
}
|
||||
|
||||
//explicitly add keys here, because Configuration.getKeys doesn't return the keys from the default configuration
|
||||
for (String key : defaults.getKeys()) {
|
||||
config.set(key, config.get(key));
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
public MojangResolver getResolver() {
|
||||
|
||||
@@ -24,7 +24,6 @@ public abstract class ForceLoginManagement<P extends C, C, L extends LoginSessio
|
||||
}
|
||||
|
||||
AuthStorage storage = core.getStorage();
|
||||
|
||||
StoredProfile playerProfile = session.getProfile();
|
||||
try {
|
||||
if (isOnlineMode()) {
|
||||
@@ -85,8 +84,8 @@ public abstract class ForceLoginManagement<P extends C, C, L extends LoginSessio
|
||||
|
||||
public boolean forceLogin(P player) {
|
||||
core.getPlugin().getLog().info("Logging player {} in", getName(player));
|
||||
boolean success = core.getAuthPluginHook().forceLogin(player);
|
||||
|
||||
boolean success = core.getAuthPluginHook().forceLogin(player);
|
||||
if (success) {
|
||||
core.sendLocaleMessage("auto-login", player);
|
||||
}
|
||||
|
||||
@@ -166,6 +166,7 @@ driver: org.sqlite.JDBC
|
||||
database: '{pluginDir}/FastLogin.db'
|
||||
|
||||
# MySQL/MariaDB
|
||||
# If you want to enable it uncomment only the lines below this not this line.
|
||||
#driver: com.mysql.jdbc.Driver
|
||||
#host: 127.0.0.1
|
||||
#port: 3306
|
||||
|
||||
16
pom.xml
16
pom.xml
@@ -8,11 +8,11 @@
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>FastLogin</name>
|
||||
<version>1.11</version>
|
||||
<version>1.11-SNAPSHOT</version>
|
||||
|
||||
<url>https://www.spigotmc.org/resources/fastlogin.14153/</url>
|
||||
<description>
|
||||
Automatically logins premium (paid accounts) player on a offline mode server
|
||||
Automatically login premium (paid accounts) player on a offline mode server
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
@@ -31,6 +31,18 @@
|
||||
<module>bungee</module>
|
||||
</modules>
|
||||
|
||||
<!--Deployment configuration for the Maven repository-->
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>codemc-snapshots</id>
|
||||
<url>https://repo.codemc.org/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
<repository>
|
||||
<id>codemc-releases</id>
|
||||
<url>https://repo.codemc.org/repository/maven-releases/</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
|
||||
<build>
|
||||
<!--Just use the project name to replace an old version of the plugin if the user does only copy-paste-->
|
||||
<finalName>${project.name}</finalName>
|
||||
|
||||
Reference in New Issue
Block a user