mirror of
https://github.com/TuxCoding/FastLogin.git
synced 2025-07-29 18:27:36 +02:00
More shared project code for less errors and less duplication
This commit is contained in:
@ -39,7 +39,7 @@ public class BukkitCore extends FastLoginCore {
|
||||
private final FastLoginBukkit plugin;
|
||||
|
||||
public BukkitCore(FastLoginBukkit plugin) {
|
||||
super(BukkitCore.<String, Object>buildCache(5, 0));
|
||||
super(BukkitCore.<String, Object>buildCache(5, 0), plugin.getConfig().getValues(false));
|
||||
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
package com.github.games647.fastlogin.bukkit.hooks;
|
||||
|
||||
import com.github.games647.fastlogin.core.AuthPlugin;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Represents a supporting authentication plugin in Bukkit/Spigot/... servers
|
||||
*/
|
||||
public interface BukkitAuthPlugin {
|
||||
public interface BukkitAuthPlugin extends AuthPlugin<Player> {
|
||||
|
||||
/**
|
||||
* Login the premium (paid account) player after
|
||||
|
@ -1,25 +1,18 @@
|
||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
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.core.JoinManagement;
|
||||
import com.github.games647.fastlogin.core.PlayerProfile;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class NameCheckTask implements Runnable {
|
||||
|
||||
private static final int VERIFY_TOKEN_LENGTH = 4;
|
||||
public class NameCheckTask extends JoinManagement<Player, ProtocolLibLoginSource> implements Runnable {
|
||||
|
||||
private final FastLoginBukkit plugin;
|
||||
private final PacketEvent packetEvent;
|
||||
@ -30,6 +23,8 @@ public class NameCheckTask implements Runnable {
|
||||
private final String username;
|
||||
|
||||
public NameCheckTask(FastLoginBukkit plugin, PacketEvent packetEvent, Random random, Player player, String username) {
|
||||
super(plugin.getCore(), plugin.getAuthPlugin());
|
||||
|
||||
this.plugin = plugin;
|
||||
this.packetEvent = packetEvent;
|
||||
this.random = random;
|
||||
@ -40,120 +35,40 @@ public class NameCheckTask implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
nameCheck();
|
||||
super.onLogin(username, new ProtocolLibLoginSource(plugin, packetEvent, player, random));
|
||||
} finally {
|
||||
ProtocolLibrary.getProtocolManager().getAsynchronousManager().signalPacketTransmission(packetEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private void nameCheck() {
|
||||
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(username);
|
||||
if (profile == null) {
|
||||
//minecraft server implementation
|
||||
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L161
|
||||
@Override
|
||||
public void requestPremiumLogin(ProtocolLibLoginSource source, PlayerProfile profile, String username, boolean registered) {
|
||||
try {
|
||||
source.setOnlineMode();
|
||||
} catch (Exception ex) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Cannot send encryption packet. Falling back to cracked login", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (profile.getUserId() == -1) {
|
||||
UUID premiumUUID = null;
|
||||
String ip = player.getAddress().getAddress().getHostAddress();
|
||||
core.getPendingLogins().put(ip + username, new Object());
|
||||
|
||||
String ip = player.getAddress().getAddress().getHostAddress();
|
||||
if (plugin.getCore().getPendingLogins().containsKey(ip + username)
|
||||
&& plugin.getConfig().getBoolean("secondAttemptCracked")) {
|
||||
plugin.getLogger().log(Level.INFO, "Second attempt login -> cracked {0}", username);
|
||||
String serverId = source.getServerId();
|
||||
byte[] verify = source.getVerifyToken();
|
||||
|
||||
//first login request failed so make a cracked session
|
||||
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
|
||||
plugin.getSessions().put(player.getAddress().toString(), loginSession);
|
||||
return;
|
||||
}
|
||||
|
||||
//user not exists in the db
|
||||
try {
|
||||
boolean isRegistered = plugin.getAuthPlugin().isRegistered(username);
|
||||
if (plugin.getConfig().getBoolean("nameChangeCheck")
|
||||
|| (plugin.getConfig().getBoolean("autoRegister") && !isRegistered)) {
|
||||
premiumUUID = plugin.getCore().getMojangApiConnector().getPremiumUUID(username);
|
||||
}
|
||||
|
||||
if (premiumUUID != null && plugin.getConfig().getBoolean("nameChangeCheck")) {
|
||||
PlayerProfile uuidProfile = plugin.getCore().getStorage().loadProfile(premiumUUID);
|
||||
if (uuidProfile != null) {
|
||||
plugin.getLogger().log(Level.FINER, "Player {0} changed it's username", premiumUUID);
|
||||
|
||||
//update the username to the new one in the database
|
||||
uuidProfile.setPlayerName(username);
|
||||
|
||||
enablePremiumLogin(uuidProfile, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (premiumUUID != null && plugin.getConfig().getBoolean("autoRegister") && !isRegistered) {
|
||||
plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
|
||||
enablePremiumLogin(profile, false);
|
||||
return;
|
||||
}
|
||||
|
||||
//no premium check passed so we save it as a cracked player
|
||||
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
|
||||
plugin.getSessions().put(player.getAddress().toString(), loginSession);
|
||||
} catch (Exception ex) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Failed to query isRegistered", ex);
|
||||
}
|
||||
} else if (profile.isPremium()) {
|
||||
enablePremiumLogin(profile, true);
|
||||
} else {
|
||||
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
|
||||
plugin.getSessions().put(player.getAddress().toString(), loginSession);
|
||||
BukkitLoginSession playerSession = new BukkitLoginSession(username, serverId, verify, registered, profile);
|
||||
plugin.getSessions().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);
|
||||
}
|
||||
}
|
||||
|
||||
//minecraft server implementation
|
||||
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L161
|
||||
private void enablePremiumLogin(PlayerProfile profile, 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);
|
||||
|
||||
//generate a random token which should be the same when we receive it from the client
|
||||
byte[] verify = new byte[VERIFY_TOKEN_LENGTH];
|
||||
random.nextBytes(verify);
|
||||
|
||||
boolean success = sentEncryptionRequest(player, serverId, verify);
|
||||
if (success) {
|
||||
String ip = player.getAddress().getAddress().getHostAddress();
|
||||
plugin.getCore().getPendingLogins().put(ip + username, new Object());
|
||||
|
||||
BukkitLoginSession playerSession = new BukkitLoginSession(username, serverId, verify, registered, profile);
|
||||
plugin.getSessions().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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean sentEncryptionRequest(Player player, String serverId, byte[] verifyToken) {
|
||||
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
|
||||
try {
|
||||
/**
|
||||
* Packet Information: http://wiki.vg/Protocol#Encryption_Request
|
||||
*
|
||||
* ServerID="" (String) key=public server key verifyToken=random 4 byte array
|
||||
*/
|
||||
PacketContainer newPacket = protocolManager.createPacket(PacketType.Login.Server.ENCRYPTION_BEGIN);
|
||||
|
||||
newPacket.getStrings().write(0, serverId);
|
||||
newPacket.getSpecificModifier(PublicKey.class).write(0, plugin.getServerKey().getPublic());
|
||||
|
||||
newPacket.getByteArrays().write(0, verifyToken);
|
||||
|
||||
//serverId is a empty string
|
||||
protocolManager.sendServerPacket(player, newPacket);
|
||||
return true;
|
||||
} catch (InvocationTargetException ex) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Cannot send encryption packet. Falling back to normal login", ex);
|
||||
}
|
||||
|
||||
return false;
|
||||
@Override
|
||||
public void startCrackedSession(ProtocolLibLoginSource source, PlayerProfile profile, String username) {
|
||||
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
|
||||
plugin.getSessions().put(player.getAddress().toString(), loginSession);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,99 @@
|
||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
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.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.LoginSource;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class ProtocolLibLoginSource implements LoginSource {
|
||||
|
||||
private static final int VERIFY_TOKEN_LENGTH = 4;
|
||||
|
||||
private final FastLoginBukkit plugin;
|
||||
|
||||
private final PacketEvent packetEvent;
|
||||
private final Player player;
|
||||
|
||||
private final Random random;
|
||||
|
||||
private String serverId;
|
||||
private byte[] verifyToken = new byte[VERIFY_TOKEN_LENGTH];
|
||||
|
||||
public ProtocolLibLoginSource(FastLoginBukkit plugin, PacketEvent packetEvent, Player player, Random random) {
|
||||
this.plugin = plugin;
|
||||
this.packetEvent = packetEvent;
|
||||
this.player = player;
|
||||
this.random = random;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnlineMode() throws Exception {
|
||||
//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/
|
||||
serverId = Long.toString(random.nextLong(), 16);
|
||||
|
||||
//generate a random token which should be the same when we receive it from the client
|
||||
random.nextBytes(verifyToken);
|
||||
|
||||
sentEncryptionRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kick(String message) throws Exception {
|
||||
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
|
||||
|
||||
PacketContainer kickPacket = protocolManager.createPacket(PacketType.Login.Server.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();
|
||||
}
|
||||
|
||||
private void sentEncryptionRequest() throws InvocationTargetException {
|
||||
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
|
||||
/**
|
||||
* Packet Information: http://wiki.vg/Protocol#Encryption_Request
|
||||
*
|
||||
* ServerID="" (String) key=public server key verifyToken=random 4 byte array
|
||||
*/
|
||||
PacketContainer newPacket = protocolManager.createPacket(PacketType.Login.Server.ENCRYPTION_BEGIN);
|
||||
|
||||
newPacket.getStrings().write(0, serverId);
|
||||
newPacket.getSpecificModifier(PublicKey.class).write(0, plugin.getServerKey().getPublic());
|
||||
|
||||
newPacket.getByteArrays().write(0, verifyToken);
|
||||
|
||||
//serverId is a empty string
|
||||
protocolManager.sendServerPacket(player, newPacket);
|
||||
}
|
||||
|
||||
public String getServerId() {
|
||||
return serverId;
|
||||
}
|
||||
|
||||
public byte[] getVerifyToken() {
|
||||
return verifyToken;
|
||||
}
|
||||
}
|
@ -5,12 +5,11 @@ import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.logging.Level;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
@ -52,7 +51,7 @@ public class StartPacketListener extends PacketAdapter {
|
||||
*/
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent packetEvent) {
|
||||
if (packetEvent.isCancelled()) {
|
||||
if (packetEvent.isCancelled() || plugin.getAuthPlugin() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -72,11 +71,6 @@ public class StartPacketListener extends PacketAdapter {
|
||||
String username = packet.getGameProfiles().read(0).getName();
|
||||
plugin.getLogger().log(Level.FINER, "Player {0} with {1} connecting", new Object[]{sessionKey, username});
|
||||
|
||||
BukkitAuthPlugin authPlugin = plugin.getAuthPlugin();
|
||||
if (authPlugin == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
packetEvent.getAsyncMarker().incrementProcessingDelay();
|
||||
NameCheckTask nameCheckTask = new NameCheckTask(plugin, packetEvent, random, player, username);
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, nameCheckTask);
|
||||
|
@ -0,0 +1,35 @@
|
||||
package com.github.games647.fastlogin.bukkit.listener.protocolsupport;
|
||||
|
||||
import com.github.games647.fastlogin.core.LoginSource;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import protocolsupport.api.events.PlayerLoginStartEvent;
|
||||
|
||||
public class ProtocolLoginSource implements LoginSource {
|
||||
|
||||
private final PlayerLoginStartEvent loginStartEvent;
|
||||
|
||||
public ProtocolLoginSource(PlayerLoginStartEvent loginStartEvent) {
|
||||
this.loginStartEvent = loginStartEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnlineMode() {
|
||||
loginStartEvent.setOnlineMode(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kick(String message) {
|
||||
loginStartEvent.denyLogin(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getAddress() {
|
||||
return loginStartEvent.getAddress();
|
||||
}
|
||||
|
||||
public PlayerLoginStartEvent getLoginStartEvent() {
|
||||
return loginStartEvent;
|
||||
}
|
||||
}
|
@ -2,31 +2,32 @@ package com.github.games647.fastlogin.bukkit.listener.protocolsupport;
|
||||
|
||||
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.JoinManagement;
|
||||
import com.github.games647.fastlogin.core.PlayerProfile;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
import protocolsupport.api.events.PlayerLoginStartEvent;
|
||||
import protocolsupport.api.events.PlayerPropertiesResolveEvent;
|
||||
|
||||
public class ProtocolSupportListener implements Listener {
|
||||
public class ProtocolSupportListener extends JoinManagement<Player, ProtocolLoginSource> implements Listener {
|
||||
|
||||
protected final FastLoginBukkit plugin;
|
||||
|
||||
public ProtocolSupportListener(FastLoginBukkit plugin) {
|
||||
super(plugin.getCore(), plugin.getAuthPlugin());
|
||||
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onLoginStart(PlayerLoginStartEvent loginStartEvent) {
|
||||
plugin.setServerStarted();
|
||||
if (loginStartEvent.isLoginDenied()) {
|
||||
if (loginStartEvent.isLoginDenied() || plugin.getAuthPlugin() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -36,68 +37,7 @@ public class ProtocolSupportListener implements Listener {
|
||||
//remove old data every time on a new login in order to keep the session only for one person
|
||||
plugin.getSessions().remove(address.toString());
|
||||
|
||||
BukkitAuthPlugin authPlugin = plugin.getAuthPlugin();
|
||||
if (authPlugin == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(username);
|
||||
if (profile != null) {
|
||||
if (profile.getUserId() == -1) {
|
||||
|
||||
String ip = address.getAddress().getHostAddress();
|
||||
if (plugin.getCore().getPendingLogins().containsKey(ip + username)
|
||||
&& plugin.getConfig().getBoolean("secondAttemptCracked")) {
|
||||
plugin.getLogger().log(Level.INFO, "Second attempt login -> cracked {0}", username);
|
||||
|
||||
//first login request failed so make a cracked session
|
||||
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
|
||||
plugin.getSessions().put(address.toString(), loginSession);
|
||||
return;
|
||||
}
|
||||
|
||||
UUID premiumUUID = null;
|
||||
|
||||
//user not exists in the db
|
||||
try {
|
||||
boolean isRegistered = plugin.getAuthPlugin().isRegistered(username);
|
||||
if (plugin.getConfig().getBoolean("nameChangeCheck")
|
||||
|| (plugin.getConfig().getBoolean("autoRegister") && !isRegistered)) {
|
||||
premiumUUID = plugin.getCore().getMojangApiConnector().getPremiumUUID(username);
|
||||
}
|
||||
|
||||
if (premiumUUID != null && plugin.getConfig().getBoolean("nameChangeCheck")) {
|
||||
PlayerProfile uuidProfile = plugin.getCore().getStorage().loadProfile(premiumUUID);
|
||||
if (uuidProfile != null) {
|
||||
plugin.getLogger().log(Level.FINER, "Player {0} changed it's username", premiumUUID);
|
||||
|
||||
//update the username to the new one in the database
|
||||
uuidProfile.setPlayerName(username);
|
||||
|
||||
startPremiumSession(username, loginStartEvent, false, uuidProfile);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (premiumUUID != null && plugin.getConfig().getBoolean("autoRegister") && !isRegistered) {
|
||||
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(address.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(address.toString(), loginSession);
|
||||
}
|
||||
}
|
||||
super.onLogin(username, new ProtocolLoginSource(loginStartEvent));
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
@ -115,18 +55,23 @@ public class ProtocolSupportListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
private void startPremiumSession(String username, PlayerLoginStartEvent loginStartEvent, boolean registered
|
||||
, PlayerProfile playerProfile) {
|
||||
loginStartEvent.setOnlineMode(true);
|
||||
InetSocketAddress address = loginStartEvent.getAddress();
|
||||
@Override
|
||||
public void requestPremiumLogin(ProtocolLoginSource source, PlayerProfile profile, String username, boolean registered) {
|
||||
source.setOnlineMode();
|
||||
|
||||
String ip = address.getAddress().getHostAddress();
|
||||
String ip = source.getAddress().getAddress().getHostAddress();
|
||||
plugin.getCore().getPendingLogins().put(ip + username, new Object());
|
||||
|
||||
BukkitLoginSession playerSession = new BukkitLoginSession(username, null, null, registered, playerProfile);
|
||||
plugin.getSessions().put(address.toString(), playerSession);
|
||||
BukkitLoginSession playerSession = new BukkitLoginSession(username, null, null, registered, profile);
|
||||
plugin.getSessions().put(source.getAddress().toString(), playerSession);
|
||||
if (plugin.getConfig().getBoolean("premiumUuid")) {
|
||||
loginStartEvent.setUseOnlineModeUUID(true);
|
||||
source.getLoginStartEvent().setUseOnlineModeUUID(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startCrackedSession(ProtocolLoginSource source, PlayerProfile profile, String username) {
|
||||
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
|
||||
plugin.getSessions().put(source.getAddress().toString(), loginSession);
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,15 @@ package com.github.games647.fastlogin.bungee;
|
||||
|
||||
import com.github.games647.fastlogin.core.FastLoginCore;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.collect.Maps;
|
||||
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.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
@ -24,7 +27,8 @@ public class BungeeCore extends FastLoginCore {
|
||||
private final FastLoginBungee plugin;
|
||||
|
||||
public BungeeCore(FastLoginBungee plugin) {
|
||||
super(CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).<String, Object>build().asMap());
|
||||
super(CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).<String, Object>build().asMap()
|
||||
, generateConfigMap(plugin.getConfig()));
|
||||
|
||||
this.plugin = plugin;
|
||||
}
|
||||
@ -90,4 +94,14 @@ public class BungeeCore extends FastLoginCore {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, Object> generateConfigMap(Configuration config) {
|
||||
Map<String, Object> configMap = Maps.newHashMap();
|
||||
Collection<String> keys = config.getKeys();
|
||||
for (String key : keys) {
|
||||
configMap.put(key, config.get(key));
|
||||
}
|
||||
|
||||
return configMap;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,35 @@
|
||||
package com.github.games647.fastlogin.bungee;
|
||||
|
||||
import com.github.games647.fastlogin.core.LoginSource;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
|
||||
public class BungeeLoginSource implements LoginSource {
|
||||
|
||||
private final PendingConnection connection;
|
||||
|
||||
public BungeeLoginSource(PendingConnection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnlineMode() {
|
||||
connection.setOnlineMode(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kick(String message) {
|
||||
connection.disconnect(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getAddress() {
|
||||
return connection.getAddress();
|
||||
}
|
||||
|
||||
public PendingConnection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
}
|
@ -1,11 +1,12 @@
|
||||
package com.github.games647.fastlogin.bungee.hooks;
|
||||
|
||||
import com.github.games647.fastlogin.core.AuthPlugin;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
/**
|
||||
* Represents a supporting authentication plugin in BungeeCord/Waterfall/... servers
|
||||
*/
|
||||
public interface BungeeAuthPlugin {
|
||||
public interface BungeeAuthPlugin extends AuthPlugin<ProxiedPlayer> {
|
||||
|
||||
/**
|
||||
* Login the premium (paid account) player after
|
||||
|
@ -1,22 +1,23 @@
|
||||
package com.github.games647.fastlogin.bungee.tasks;
|
||||
|
||||
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
|
||||
import com.github.games647.fastlogin.bungee.BungeeLoginSource;
|
||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||
import com.github.games647.fastlogin.bungee.hooks.BungeeAuthPlugin;
|
||||
import com.github.games647.fastlogin.core.JoinManagement;
|
||||
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.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.PreLoginEvent;
|
||||
|
||||
public class AsyncPremiumCheck implements Runnable {
|
||||
public class AsyncPremiumCheck extends JoinManagement<ProxiedPlayer, BungeeLoginSource> implements Runnable {
|
||||
|
||||
private final FastLoginBungee plugin;
|
||||
private final PreLoginEvent preLoginEvent;
|
||||
|
||||
public AsyncPremiumCheck(FastLoginBungee plugin, PreLoginEvent preLoginEvent) {
|
||||
super(plugin.getCore(), plugin.getBungeeAuthPlugin());
|
||||
|
||||
this.plugin = plugin;
|
||||
this.preLoginEvent = preLoginEvent;
|
||||
}
|
||||
@ -27,89 +28,24 @@ public class AsyncPremiumCheck implements Runnable {
|
||||
plugin.getSession().remove(connection);
|
||||
|
||||
String username = connection.getName();
|
||||
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(username);
|
||||
if (profile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (profile.getUserId() == -1) {
|
||||
String ip = connection.getAddress().getAddress().getHostAddress();
|
||||
if (plugin.getCore().getPendingLogins().containsKey(ip + username)
|
||||
&& plugin.getConfig().getBoolean("secondAttemptCracked")) {
|
||||
plugin.getLogger().log(Level.INFO, "Second attempt login -> cracked {0}", username);
|
||||
|
||||
//first login request failed so make a cracked session
|
||||
plugin.getSession().put(connection, new BungeeLoginSession(username, false, profile));
|
||||
return;
|
||||
}
|
||||
|
||||
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 BungeeLoginSession(username, false, profile));
|
||||
}
|
||||
} else if (profile.isPremium()) {
|
||||
requestPremiumLogin(connection, profile, username, true);
|
||||
} else {
|
||||
if (plugin.getConfig().getBoolean("switchMode")) {
|
||||
connection.disconnect(plugin.getCore().getMessage("switch-kick-message"));
|
||||
return;
|
||||
}
|
||||
|
||||
//Cracked session
|
||||
plugin.getSession().put(connection, new BungeeLoginSession(username, false, profile));
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Failed to check premium state", ex);
|
||||
super.onLogin(username, new BungeeLoginSource(connection));
|
||||
} 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;
|
||||
}
|
||||
@Override
|
||||
public void requestPremiumLogin(BungeeLoginSource source, PlayerProfile profile, String username, boolean registered) {
|
||||
source.setOnlineMode();
|
||||
plugin.getSession().put(source.getConnection(), new BungeeLoginSession(username, registered, profile));
|
||||
|
||||
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);
|
||||
|
||||
//update the username to the new one in the database
|
||||
profile.setPlayerName(username);
|
||||
|
||||
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 BungeeLoginSession(username, register, profile));
|
||||
|
||||
String ip = con.getAddress().getAddress().getHostAddress();
|
||||
String ip = source.getAddress().getAddress().getHostAddress();
|
||||
plugin.getCore().getPendingLogins().put(ip + username, new Object());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startCrackedSession(BungeeLoginSource source, PlayerProfile profile, String username) {
|
||||
plugin.getSession().put(source.getConnection(), new BungeeLoginSession(username, false, profile));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
public interface AuthPlugin<T> {
|
||||
|
||||
boolean forceLogin(T player);
|
||||
|
||||
boolean forceRegister(T player, String password);
|
||||
|
||||
boolean isRegistered(String playerName) throws Exception;
|
||||
}
|
@ -33,12 +33,14 @@ public abstract class FastLoginCore {
|
||||
protected final Map<String, String> localeMessages = new ConcurrentHashMap<>();
|
||||
|
||||
private final ConcurrentMap<String, Object> pendingLogins;
|
||||
private final SharedConfig sharedConfig;
|
||||
|
||||
private MojangApiConnector mojangApiConnector;
|
||||
private AuthStorage storage;
|
||||
|
||||
public FastLoginCore(ConcurrentMap<String, Object> pendingLogins) {
|
||||
public FastLoginCore(ConcurrentMap<String, Object> pendingLogins, Map<String, Object> config) {
|
||||
this.pendingLogins = pendingLogins;
|
||||
this.sharedConfig = new SharedConfig(config);
|
||||
}
|
||||
|
||||
public void setMojangApiConnector(MojangApiConnector mojangApiConnector) {
|
||||
@ -118,6 +120,10 @@ public abstract class FastLoginCore {
|
||||
return false;
|
||||
}
|
||||
|
||||
public SharedConfig getSharedConfig() {
|
||||
return sharedConfig;
|
||||
}
|
||||
|
||||
public ConcurrentMap<String, Object> getPendingLogins() {
|
||||
return pendingLogins;
|
||||
}
|
||||
|
@ -0,0 +1,96 @@
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public abstract class JoinManagement<T, S extends LoginSource> {
|
||||
|
||||
protected final FastLoginCore core;
|
||||
protected final AuthPlugin<T> authHook;
|
||||
|
||||
public JoinManagement(FastLoginCore core, AuthPlugin<T> authHook) {
|
||||
this.core = core;
|
||||
this.authHook = authHook;
|
||||
}
|
||||
|
||||
public void onLogin(String username, S source) {
|
||||
PlayerProfile profile = core.getStorage().loadProfile(username);
|
||||
if (profile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
SharedConfig sharedConfig = core.getSharedConfig();
|
||||
|
||||
String ip = source.getAddress().getAddress().getHostAddress();
|
||||
try {
|
||||
if (profile.getUserId() == -1) {
|
||||
if (core.getPendingLogins().containsKey(ip + username)
|
||||
&& sharedConfig.get("secondAttemptCracked", false)) {
|
||||
core.getLogger().log(Level.INFO, "Second attempt login -> cracked {0}", username);
|
||||
|
||||
//first login request failed so make a cracked session
|
||||
startCrackedSession(source, profile, username);
|
||||
return;
|
||||
}
|
||||
|
||||
UUID premiumUUID = null;
|
||||
if (sharedConfig.get("nameChangeCheck", false) || sharedConfig.get("autoRegister", false)) {
|
||||
premiumUUID = core.getMojangApiConnector().getPremiumUUID(username);
|
||||
}
|
||||
|
||||
if (premiumUUID == null
|
||||
|| (!checkNameChange(premiumUUID, source, username)
|
||||
&& !checkPremiumName(username, source, profile))) {
|
||||
//nothing detected the player as premium -> start a cracked session
|
||||
startCrackedSession(source, profile, username);
|
||||
}
|
||||
} else if (profile.isPremium()) {
|
||||
requestPremiumLogin(source, profile, username, true);
|
||||
} else {
|
||||
if (core.getSharedConfig().get("switchMode", false)) {
|
||||
source.kick(core.getMessage("switch-kick-message"));
|
||||
return;
|
||||
}
|
||||
|
||||
startCrackedSession(source, profile, username);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
core.getLogger().log(Level.SEVERE, "Failed to check premium state", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkPremiumName(String username, S source, PlayerProfile profile)
|
||||
throws Exception {
|
||||
if (core.getSharedConfig().get("autoRegister", false)
|
||||
&& (authHook == null || !authHook.isRegistered(username))) {
|
||||
core.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
|
||||
requestPremiumLogin(source, profile, username, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean checkNameChange(UUID premiumUUID, S source, String username) {
|
||||
//user not exists in the db
|
||||
if (core.getSharedConfig().get("nameChangeCheck", false)) {
|
||||
PlayerProfile profile = core.getStorage().loadProfile(premiumUUID);
|
||||
if (profile != null) {
|
||||
//uuid exists in the database
|
||||
core.getLogger().log(Level.FINER, "Player {0} changed it's username", premiumUUID);
|
||||
|
||||
//update the username to the new one in the database
|
||||
profile.setPlayerName(username);
|
||||
|
||||
requestPremiumLogin(source, profile, username, false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract void requestPremiumLogin(S source, PlayerProfile profile, String username, boolean registered);
|
||||
|
||||
public abstract void startCrackedSession(S source, PlayerProfile profile, String username);
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
public interface LoginSource {
|
||||
|
||||
void setOnlineMode() throws Exception;
|
||||
|
||||
void kick(String message) throws Exception;
|
||||
|
||||
InetSocketAddress getAddress();
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class SharedConfig {
|
||||
|
||||
private final Map<String, Object> configValues;
|
||||
|
||||
public SharedConfig(Map<String, Object> configValues) {
|
||||
this.configValues = configValues;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(String path, T def) {
|
||||
Object val = configValues.get(path);
|
||||
return ( val != null ) ? (T) val : def;
|
||||
}
|
||||
|
||||
public <T> T get(String path) {
|
||||
return get(path, null);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user