diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/commands/CrackedCommand.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/commands/CrackedCommand.java index 1f881b14..062d77da 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/commands/CrackedCommand.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/commands/CrackedCommand.java @@ -22,11 +22,6 @@ public class CrackedCommand implements CommandExecutor { @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - if (plugin.getStorage() == null) { - sender.sendMessage(ChatColor.DARK_RED + "This command is disabled on the backend server"); - return true; - } - if (args.length == 0) { if (!(sender instanceof Player)) { //console or command block @@ -37,22 +32,25 @@ public class CrackedCommand implements CommandExecutor { final Player player = (Player) sender; // UUID uuid = player.getUniqueId(); - //todo: load async if it's not in the cache anymore - final PlayerProfile profile = plugin.getStorage().getProfile(player.getName(), true); - if (profile.isPremium()) { - sender.sendMessage(ChatColor.DARK_GREEN + "Removed from the list of premium players"); - profile.setPremium(false); - profile.setUuid(null); - Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() { - @Override - public void run() { - plugin.getStorage().save(profile); - } - }); - + if (plugin.isBungeeCord()) { notifiyBungeeCord((Player) sender); + sender.sendMessage(ChatColor.YELLOW + "Sending request..."); } else { - sender.sendMessage(ChatColor.DARK_RED + "You are not in the premium list"); + //todo: load async if it's not in the cache anymore + final PlayerProfile profile = plugin.getStorage().getProfile(player.getName(), true); + if (profile.isPremium()) { + sender.sendMessage(ChatColor.DARK_GREEN + "Removed from the list of premium players"); + profile.setPremium(false); + profile.setUuid(null); + Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() { + @Override + public void run() { + plugin.getStorage().save(profile); + } + }); + } else { + sender.sendMessage(ChatColor.DARK_RED + "You are not in the premium list"); + } } return true; diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/commands/PremiumCommand.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/commands/PremiumCommand.java index e8731d64..2e504d96 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/commands/PremiumCommand.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/commands/PremiumCommand.java @@ -27,11 +27,6 @@ public class PremiumCommand implements CommandExecutor { @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - if (plugin.getStorage() == null) { - sender.sendMessage(ChatColor.DARK_RED + "This command is disabled on the backend server"); - return true; - } - if (args.length == 0) { if (!(sender instanceof Player)) { //console or command block @@ -42,23 +37,28 @@ public class PremiumCommand implements CommandExecutor { Player player = (Player) sender; // UUID uuid = player.getUniqueId(); -// //todo: load async if it's not in the cache anymore - final PlayerProfile profile = plugin.getStorage().getProfile(player.getName(), true); - if (profile.isPremium()) { - sender.sendMessage(ChatColor.DARK_RED + "You are already on the premium list"); + if (plugin.isBungeeCord()) { + notifiyBungeeCord(player); + sender.sendMessage(ChatColor.YELLOW + "Sending request..."); } else { - //todo: resolve uuid - profile.setPremium(true); - Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() { - @Override - public void run() { - plugin.getStorage().save(profile); - } - }); - sender.sendMessage(ChatColor.DARK_GREEN + "Added to the list of premium players"); +// //todo: load async if it's not in the cache anymore + final PlayerProfile profile = plugin.getStorage().getProfile(player.getName(), true); + if (profile.isPremium()) { + sender.sendMessage(ChatColor.DARK_RED + "You are already on the premium list"); + } else { + //todo: resolve uuid + profile.setPremium(true); + Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() { + @Override + public void run() { + plugin.getStorage().save(profile); + } + }); + + sender.sendMessage(ChatColor.DARK_GREEN + "Added to the list of premium players"); + } } - notifiyBungeeCord((Player) sender); return true; } else { sender.sendMessage(ChatColor.DARK_RED + "NOT IMPLEMENTED YET"); diff --git a/bukkit/src/main/resources/config.yml b/bukkit/src/main/resources/config.yml index b9c9bb29..279df83b 100644 --- a/bukkit/src/main/resources/config.yml +++ b/bukkit/src/main/resources/config.yml @@ -53,7 +53,7 @@ premiumUuid: false # players like cracked player, you have to use other plugins. # # If you want to use skins for your cracked player, you need an additional plugin like -# ChangeSkin, SKinRestoer, ... +# ChangeSkin, SkinRestoer, ... forwardSkin: true # Database configuration diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/FastLoginBungee.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/FastLoginBungee.java index 18288657..da62302f 100644 --- a/bungee/src/main/java/com/github/games647/fastlogin/bungee/FastLoginBungee.java +++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/FastLoginBungee.java @@ -7,6 +7,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; +import java.util.Random; import java.util.UUID; import java.util.logging.Level; @@ -28,10 +29,16 @@ public class FastLoginBungee extends Plugin { + "-" + withoutDashes.substring(20, 32)); } + private static final char[] CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + .toCharArray(); + private BungeeAuthPlugin bungeeAuthPlugin; + private final MojangApiConnector mojangApiConnector = new MojangApiConnector(this); private Storage storage; private Configuration configuration; + private final Random random = new Random(); + @Override public void onEnable() { File configFile = new File(getDataFolder(), "config.yml"); @@ -75,6 +82,15 @@ public class FastLoginBungee extends Plugin { registerHook(); } + public String generateStringPassword() { + StringBuilder generatedPassword = new StringBuilder(8); + for (int i = 1; i <= 8; i++) { + generatedPassword.append(CHARACTERS[random.nextInt(CHARACTERS.length - 1)]); + } + + return generatedPassword.toString(); + } + @Override public void onDisable() { if (storage != null) { @@ -90,6 +106,10 @@ public class FastLoginBungee extends Plugin { return storage; } + public MojangApiConnector getMojangApiConnector() { + return mojangApiConnector; + } + /** * Get the auth plugin hook for BungeeCord * diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/MojangApiConnector.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/MojangApiConnector.java new file mode 100644 index 00000000..477877f9 --- /dev/null +++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/MojangApiConnector.java @@ -0,0 +1,78 @@ +package com.github.games647.fastlogin.bungee; + +import com.google.gson.Gson; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.UUID; +import java.util.logging.Level; +import java.util.regex.Pattern; +import net.md_5.bungee.BungeeCord; + +public class MojangApiConnector { + + //http connection, read timeout and user agent for a connection to mojang api servers + private static final int TIMEOUT = 1 * 1_000; + private static final String USER_AGENT = "Premium-Checker"; + + //mojang api check to prove a player is logged in minecraft and made a join server request + private static final String HAS_JOINED_URL = "https://sessionserver.mojang.com/session/minecraft/hasJoined?"; + + //only premium (paid account) users have a uuid from here + private static final String UUID_LINK = "https://api.mojang.com/users/profiles/minecraft/"; + //this includes a-zA-Z1-9_ + private static final String VALID_PLAYERNAME = "^\\w{2,16}$"; + + //compile the pattern only on plugin enable -> and this have to be threadsafe + private final Pattern playernameMatcher = Pattern.compile(VALID_PLAYERNAME); + + private final FastLoginBungee plugin; + + private final Gson gson = new Gson(); + + public MojangApiConnector(FastLoginBungee plugin) { + this.plugin = plugin; + } + + /** + * + * @param playerName + * @return null on non-premium + */ + public UUID getPremiumUUID(String playerName) { + //check if it's a valid playername + if (playernameMatcher.matcher(playerName).matches()) { + //only make a API call if the name is valid existing mojang account + try { + HttpURLConnection connection = getConnection(UUID_LINK + playerName); + if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { + BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + String line = reader.readLine(); + if (line != null && !line.equals("null")) { + MojangPlayer mojangPlayer = BungeeCord.getInstance().gson.fromJson(line, MojangPlayer.class); + return FastLoginBungee.parseId(mojangPlayer.getId()); + } + } + //204 - no content for not found + } catch (Exception ex) { + plugin.getLogger().log(Level.SEVERE, "Failed to check if player has a paid account", ex); + } + //this connection doesn't need to be closed. So can make use of keep alive in java + } + + return null; + } + + private HttpURLConnection getConnection(String url) throws IOException { + HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setConnectTimeout(TIMEOUT); + connection.setReadTimeout(TIMEOUT); + //the new Mojang API just uses json as response + connection.setRequestProperty("Content-Type", "application/json"); + connection.setRequestProperty("User-Agent", USER_AGENT); + + return connection; + } +} diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/MojangPlayer.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/MojangPlayer.java new file mode 100644 index 00000000..d028c43c --- /dev/null +++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/MojangPlayer.java @@ -0,0 +1,15 @@ +package com.github.games647.fastlogin.bungee; + +public class MojangPlayer { + + private String id; + private String name; + + public String getId() { + return id; + } + + public String getName() { + return name; + } +} diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/PlayerConnectionListener.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/PlayerConnectionListener.java index 35926921..108bf197 100644 --- a/bungee/src/main/java/com/github/games647/fastlogin/bungee/PlayerConnectionListener.java +++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/PlayerConnectionListener.java @@ -2,13 +2,19 @@ package com.github.games647.fastlogin.bungee; import com.github.games647.fastlogin.bungee.hooks.BungeeAuthPlugin; import com.google.common.base.Charsets; +import com.google.common.cache.CacheBuilder; import com.google.common.io.ByteArrayDataInput; import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteStreams; import java.util.UUID; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.connection.PendingConnection; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.Server; @@ -30,6 +36,10 @@ import net.md_5.bungee.event.EventHandler; public class PlayerConnectionListener implements Listener { protected final FastLoginBungee plugin; + private final ConcurrentMap pendingAutoRegister = CacheBuilder + .newBuilder() + .expireAfterWrite(1, TimeUnit.MINUTES) + .build().asMap(); public PlayerConnectionListener(FastLoginBungee plugin) { this.plugin = plugin; @@ -49,14 +59,15 @@ public class PlayerConnectionListener implements Listener { if (playerProfile != null) { //user not exists in the db if (!playerProfile.isPremium() && playerProfile.getUserId() == -1) { -// BungeeAuthPlugin authPlugin = plugin.getBungeeAuthPlugin(); -// if (plugin.getConfiguration().getBoolean("autoRegister") && !authPlugin.isRegistered(username)) { -// UUID premiumUUID = plugin.getApiConnector().getPremiumUUID(username); -// if (premiumUUID != null) { -// plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username); -// connection.setOnlineMode(true); -// } -// } + BungeeAuthPlugin authPlugin = plugin.getBungeeAuthPlugin(); + if (plugin.getConfiguration().getBoolean("autoRegister") && !authPlugin.isRegistered(username)) { + UUID premiumUUID = plugin.getMojangApiConnector().getPremiumUUID(username); + if (premiumUUID != null) { + plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username); + connection.setOnlineMode(true); + pendingAutoRegister.put(connection, new Object()); + } + } } else if (playerProfile.isPremium()) { connection.setOnlineMode(true); } @@ -108,7 +119,13 @@ public class PlayerConnectionListener implements Listener { BungeeAuthPlugin authPlugin = plugin.getBungeeAuthPlugin(); if (authPlugin != null) { - authPlugin.forceLogin(player); + Object existed = pendingAutoRegister.remove(player.getPendingConnection()); + if (existed == null) { + authPlugin.forceLogin(player); + } else { + String password = plugin.generateStringPassword(); + authPlugin.forceRegister(player, password); + } } } } @@ -136,6 +153,16 @@ public class PlayerConnectionListener implements Listener { @Override public void run() { PlayerProfile playerProfile = plugin.getStorage().getProfile(forPlayer.getName(), true); + if (playerProfile.isPremium()) { + if (forPlayer.isConnected()) { + TextComponent textComponent = new TextComponent("You are already on the premium list"); + textComponent.setColor(ChatColor.DARK_RED); + forPlayer.sendMessage(textComponent); + } + + return; + } + playerProfile.setPremium(true); //todo: set uuid plugin.getStorage().save(playerProfile); @@ -147,6 +174,16 @@ public class PlayerConnectionListener implements Listener { @Override public void run() { PlayerProfile playerProfile = plugin.getStorage().getProfile(forPlayer.getName(), true); + if (!playerProfile.isPremium()) { + if (forPlayer.isConnected()) { + TextComponent textComponent = new TextComponent("You are not in the premium list"); + textComponent.setColor(ChatColor.DARK_RED); + forPlayer.sendMessage(textComponent); + } + + return; + } + playerProfile.setPremium(false); playerProfile.setUuid(null); //todo: set uuid diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/hooks/BungeeAuthHook.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/hooks/BungeeAuthHook.java index 8f7bdde0..947af7b4 100644 --- a/bungee/src/main/java/com/github/games647/fastlogin/bungee/hooks/BungeeAuthHook.java +++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/hooks/BungeeAuthHook.java @@ -4,7 +4,6 @@ import java.lang.reflect.Method; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Random; -import java.util.concurrent.TimeUnit; import me.vik1395.BungeeAuth.ListenerClass; import me.vik1395.BungeeAuth.Main; @@ -43,13 +42,8 @@ public class BungeeAuthHook implements BungeeAuthPlugin { callProtected("setStatus", parameterTypes, arguments); ListenerClass.movePlayer(player, false); - ProxyServer.getInstance().getScheduler().schedule(Main.plugin, new Runnable() { - @Override - public void run() { - //not thread-safe - ListenerClass.prelogin.get(player.getName()).cancel(); - } - }, 0, TimeUnit.SECONDS); + //proparly not thread-safe + ListenerClass.prelogin.get(player.getName()).cancel(); } catch (Exception ex) { Main.plugin.getLogger().severe("[BungeeAuth] Error force loging in player"); } @@ -95,14 +89,8 @@ public class BungeeAuthHook implements BungeeAuthPlugin { public void run() { try { callProtected("newPlayerEntry", parameterTypes, arguments); - - ProxyServer.getInstance().getScheduler().schedule(Main.plugin, new Runnable() { - @Override - public void run() { - //proparly not thread-safe - forceLogin(player); - } - }, 0, TimeUnit.SECONDS); + //proparly not thread-safe + forceLogin(player); } catch (Exception ex) { Main.plugin.getLogger().severe("[BungeeAuth] Error when creating a new player in the Database"); }