diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/BukkitLoginSession.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/BukkitLoginSession.java index 446ece75..e67bfc52 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/BukkitLoginSession.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/BukkitLoginSession.java @@ -1,7 +1,7 @@ package com.github.games647.fastlogin.bukkit; -import com.github.games647.fastlogin.core.PlayerProfile; -import com.github.games647.fastlogin.core.mojang.SkinProperties; +import com.github.games647.craftapi.model.skin.SkinProperty; +import com.github.games647.fastlogin.core.StoredProfile; import com.github.games647.fastlogin.core.shared.LoginSession; import java.util.Optional; @@ -20,10 +20,10 @@ public class BukkitLoginSession extends LoginSession { private boolean verified; - private SkinProperties skinProperty; + private SkinProperty skinProperty; public BukkitLoginSession(String username, String serverId, byte[] verifyToken, boolean registered - , PlayerProfile profile) { + , StoredProfile profile) { super(username, registered, profile); this.serverId = serverId; @@ -36,7 +36,7 @@ public class BukkitLoginSession extends LoginSession { } //cracked player - public BukkitLoginSession(String username, PlayerProfile profile) { + public BukkitLoginSession(String username, StoredProfile profile) { this(username, "", ArrayUtils.EMPTY_BYTE_ARRAY, false, profile); } @@ -51,14 +51,14 @@ public class BukkitLoginSession extends LoginSession { return ArrayUtils.clone(verifyToken); } - public synchronized Optional getSkin() { + public synchronized Optional getSkin() { return Optional.ofNullable(skinProperty); } /** * Sets the premium skin property which was retrieved by the session server */ - public synchronized void setSkinProperty(SkinProperties skinProperty) { + public synchronized void setSkinProperty(SkinProperty skinProperty) { this.skinProperty = skinProperty; } diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java index 1ec8fa1a..65cc437b 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java @@ -11,15 +11,12 @@ import com.github.games647.fastlogin.bukkit.tasks.DelayedAuthHook; import com.github.games647.fastlogin.core.CommonUtil; import com.github.games647.fastlogin.core.messages.ChangePremiumMessage; import com.github.games647.fastlogin.core.messages.ChannelMessage; -import com.github.games647.fastlogin.core.mojang.MojangApiConnector; import com.github.games647.fastlogin.core.shared.FastLoginCore; import com.github.games647.fastlogin.core.shared.PlatformPlugin; import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteStreams; -import com.google.common.net.HostAndPort; import java.nio.file.Path; -import java.util.List; import java.util.Optional; import java.util.concurrent.ConcurrentMap; @@ -188,9 +185,4 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin addresses, int requests, List proxies) { - return new MojangApiBukkit(logger, addresses, requests, proxies); - } } diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/MojangApiBukkit.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/MojangApiBukkit.java deleted file mode 100644 index 30d2a7e8..00000000 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/MojangApiBukkit.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.github.games647.fastlogin.bukkit; - -import com.github.games647.fastlogin.core.mojang.MojangApiConnector; -import com.github.games647.fastlogin.core.mojang.SkinProperties; -import com.github.games647.fastlogin.core.mojang.VerificationReply; -import com.github.games647.fastlogin.core.shared.LoginSession; -import com.google.common.net.HostAndPort; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.InetSocketAddress; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.Collection; - -import org.slf4j.Logger; - -public class MojangApiBukkit extends MojangApiConnector { - - //mojang api check to prove a player is logged in Minecraft and made a join server request - private static final String HAS_JOINED_URL = "https://sessionserver.mojang.com/session/minecraft/hasJoined?" + - "username=%s&serverId=%s&ip=%s"; - - public MojangApiBukkit(Logger logger, Collection localAddresses, int rateLimit - , Iterable proxies) { - super(logger, localAddresses, rateLimit, proxies); - } - - @Override - public boolean hasJoinedServer(LoginSession session, String serverId, InetSocketAddress ip) { - BukkitLoginSession playerSession = (BukkitLoginSession) session; - - try { - String encodedIp = URLEncoder.encode(ip.getAddress().getHostAddress(), "UTF-8"); - String url = String.format(HAS_JOINED_URL, playerSession.getUsername(), serverId, encodedIp); - - HttpURLConnection conn = getConnection(url); - try (BufferedReader reader = new BufferedReader( - new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) { - //validate parsing - //http://wiki.vg/Protocol_Encryption#Server - VerificationReply verification = gson.fromJson(reader, VerificationReply.class); - playerSession.setUuid(verification.getId()); - - SkinProperties[] properties = verification.getProperties(); - if (properties != null && properties.length > 0) { - SkinProperties skinProperty = properties[0]; - playerSession.setSkinProperty(skinProperty); - } - - return true; - } - } catch (IOException ex) { - //catch not only io-exceptions also parse and NPE on unexpected json format - logger.warn("Failed to verify session", ex); - } - - //this connection doesn't need to be closed. So can make use of keep alive in java - return false; - } -} 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 30d8e890..c4a8e2a0 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 @@ -1,7 +1,7 @@ package com.github.games647.fastlogin.bukkit.commands; import com.github.games647.fastlogin.bukkit.FastLoginBukkit; -import com.github.games647.fastlogin.core.PlayerProfile; +import com.github.games647.fastlogin.core.StoredProfile; import org.bukkit.Bukkit; import org.bukkit.command.Command; @@ -31,7 +31,7 @@ public class CrackedCommand implements CommandExecutor { plugin.getCore().sendLocaleMessage("wait-on-proxy", sender); } else { //todo: load async if - PlayerProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName()); + StoredProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName()); if (profile.isPremium()) { plugin.getCore().sendLocaleMessage("remove-premium", sender); @@ -64,7 +64,7 @@ public class CrackedCommand implements CommandExecutor { plugin.getCore().sendLocaleMessage("wait-on-proxy", sender); } else { //todo: load async - PlayerProfile profile = plugin.getCore().getStorage().loadProfile(args[0]); + StoredProfile profile = plugin.getCore().getStorage().loadProfile(args[0]); if (profile == null) { sender.sendMessage("Error occurred"); return; 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 7addfce9..9ea89eca 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 @@ -1,7 +1,7 @@ package com.github.games647.fastlogin.bukkit.commands; import com.github.games647.fastlogin.bukkit.FastLoginBukkit; -import com.github.games647.fastlogin.core.PlayerProfile; +import com.github.games647.fastlogin.core.StoredProfile; import java.util.UUID; @@ -47,7 +47,7 @@ public class PremiumCommand implements CommandExecutor { plugin.getCore().getPendingConfirms().remove(id); //todo: load async - PlayerProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName()); + StoredProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName()); if (profile.isPremium()) { plugin.getCore().sendLocaleMessage("already-exists", sender); } else { @@ -80,7 +80,7 @@ public class PremiumCommand implements CommandExecutor { plugin.getCore().sendLocaleMessage("wait-on-proxy", sender); } else { //todo: load async - PlayerProfile profile = plugin.getCore().getStorage().loadProfile(args[0]); + StoredProfile profile = plugin.getCore().getStorage().loadProfile(args[0]); if (profile == null) { plugin.getCore().sendLocaleMessage("player-unknown", sender); return; diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/NameCheckTask.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/NameCheckTask.java index 545be923..622f4727 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/NameCheckTask.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/NameCheckTask.java @@ -4,7 +4,7 @@ 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.PlayerProfile; +import com.github.games647.fastlogin.core.StoredProfile; import com.github.games647.fastlogin.core.shared.JoinManagement; import java.security.PublicKey; @@ -49,8 +49,8 @@ public class NameCheckTask extends JoinManagement response = resolver.hasJoined(username, serverId, address); + if (response.isPresent()) { + plugin.getLog().info("GameProfile {} has a verified premium account", username); - 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(), player.getAddress(), serverId); + 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 sessionserver", ioEx); } } diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolSupportListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolSupportListener.java index a8421231..9d7db6c6 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolSupportListener.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolSupportListener.java @@ -1,9 +1,9 @@ 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.PlayerProfile; -import com.github.games647.fastlogin.core.mojang.SkinProperties; +import com.github.games647.fastlogin.core.StoredProfile; import com.github.games647.fastlogin.core.shared.JoinManagement; import java.net.InetSocketAddress; @@ -47,13 +47,13 @@ public class ProtocolSupportListener extends JoinManagement premium player - if (propertiesResolveEvent.hasProperty(SkinProperties.TEXTURE_KEY) && session != null) { + if (propertiesResolveEvent.hasProperty(SkinProperty.TEXTURE_KEY) && session != null) { session.setVerified(true); } } @Override - public void requestPremiumLogin(ProtocolLoginSource source, PlayerProfile profile, String username + public void requestPremiumLogin(ProtocolLoginSource source, StoredProfile profile, String username , boolean registered) { source.setOnlineMode(); @@ -69,7 +69,7 @@ public class ProtocolSupportListener extends JoinManagement addresses, int requests, List proxies) { - return new MojangApiConnector(logger, addresses, requests, proxies); - } } diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/listener/ConnectListener.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/listener/ConnectListener.java index 400a4c49..a7759a17 100644 --- a/bungee/src/main/java/com/github/games647/fastlogin/bungee/listener/ConnectListener.java +++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/listener/ConnectListener.java @@ -1,10 +1,10 @@ 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.core.PlayerProfile; -import com.github.games647.fastlogin.core.mojang.UUIDTypeAdapter; +import com.github.games647.fastlogin.core.StoredProfile; import com.github.games647.fastlogin.core.shared.LoginSession; import java.lang.reflect.Field; @@ -67,13 +67,13 @@ public class ConnectListener implements Listener { LoginSession session = plugin.getSession().get(connection); session.setUuid(connection.getUniqueId()); - PlayerProfile playerProfile = session.getProfile(); + StoredProfile playerProfile = session.getProfile(); playerProfile.setId(connection.getUniqueId()); //bungeecord will do this automatically so override it on disabled option if (!plugin.getCore().getConfig().get("premiumUuid", true)) { try { - UUID offlineUUID = UUIDTypeAdapter.getOfflineUUID(username); + UUID offlineUUID = UUIDAdapter.generateOfflineId(username); //bungeecord doesn't support overriding the premium uuid //so we have to do it with reflection diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/listener/MessageListener.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/listener/MessageListener.java index 40885c9b..2da1273a 100644 --- a/bungee/src/main/java/com/github/games647/fastlogin/bungee/listener/MessageListener.java +++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/listener/MessageListener.java @@ -3,7 +3,7 @@ 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.core.PlayerProfile; +import com.github.games647.fastlogin.core.StoredProfile; import com.github.games647.fastlogin.core.messages.ChangePremiumMessage; import com.github.games647.fastlogin.core.shared.FastLoginCore; import com.google.common.io.ByteArrayDataInput; @@ -86,7 +86,7 @@ public class MessageListener implements Listener { //bukkit module successfully received and force logged in the user //update only on success to prevent corrupt data BungeeLoginSession loginSession = plugin.getSession().get(forPlayer.getPendingConnection()); - PlayerProfile playerProfile = loginSession.getProfile(); + StoredProfile playerProfile = loginSession.getProfile(); loginSession.setRegistered(true); if (!loginSession.isAlreadySaved()) { diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/tasks/AsyncPremiumCheck.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/tasks/AsyncPremiumCheck.java index 608b002b..debe0b23 100644 --- a/bungee/src/main/java/com/github/games647/fastlogin/bungee/tasks/AsyncPremiumCheck.java +++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/tasks/AsyncPremiumCheck.java @@ -3,7 +3,7 @@ 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.core.PlayerProfile; +import com.github.games647.fastlogin.core.StoredProfile; import com.github.games647.fastlogin.core.shared.JoinManagement; import net.md_5.bungee.api.CommandSender; @@ -42,7 +42,7 @@ public class AsyncPremiumCheck extends JoinManagementluck-repo https://ci.lucko.me/plugin/repository/everything + + + codemc-repo + https://repo.codemc.org/repository/maven-public/ + @@ -58,6 +63,12 @@ + + com.github.games647 + craftapi + 0.1 + + com.google.guava diff --git a/core/src/main/java/com/github/games647/fastlogin/core/AuthStorage.java b/core/src/main/java/com/github/games647/fastlogin/core/AuthStorage.java index dd82a8de..88882ec9 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/AuthStorage.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/AuthStorage.java @@ -1,6 +1,6 @@ package com.github.games647.fastlogin.core; -import com.github.games647.fastlogin.core.mojang.UUIDTypeAdapter; +import com.github.games647.craftapi.UUIDAdapter; import com.github.games647.fastlogin.core.shared.FastLoginCore; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; @@ -92,14 +92,14 @@ public class AuthStorage { } } - public PlayerProfile loadProfile(String name) { + public StoredProfile loadProfile(String name) { try (Connection con = dataSource.getConnection(); PreparedStatement loadStmt = con.prepareStatement(LOAD_BY_NAME) ) { loadStmt.setString(1, name); try (ResultSet resultSet = loadStmt.executeQuery()) { - return parseResult(resultSet).orElseGet(() -> new PlayerProfile(null, name, false, "")); + return parseResult(resultSet).orElseGet(() -> new StoredProfile(null, name, false, "")); } } catch (SQLException sqlEx) { core.getPlugin().getLog().error("Failed to query profile: {}", name, sqlEx); @@ -108,11 +108,10 @@ public class AuthStorage { return null; } - public PlayerProfile loadProfile(UUID uuid) { + public StoredProfile loadProfile(UUID uuid) { try (Connection con = dataSource.getConnection(); - PreparedStatement loadStmt = con.prepareStatement(LOAD_BY_UUID) - ) { - loadStmt.setString(1, UUIDTypeAdapter.toMojangId(uuid)); + PreparedStatement loadStmt = con.prepareStatement(LOAD_BY_UUID)) { + loadStmt.setString(1, UUIDAdapter.toMojangId(uuid)); try (ResultSet resultSet = loadStmt.executeQuery()) { return parseResult(resultSet).orElse(null); @@ -124,30 +123,30 @@ public class AuthStorage { return null; } - private Optional parseResult(ResultSet resultSet) throws SQLException { + private Optional parseResult(ResultSet resultSet) throws SQLException { if (resultSet.next()) { long userId = resultSet.getInt(1); - UUID uuid = UUIDTypeAdapter.parseId(resultSet.getString(2)); + UUID uuid = UUIDAdapter.parseId(resultSet.getString(2)); String name = resultSet.getString(3); boolean premium = resultSet.getBoolean(4); String lastIp = resultSet.getString(5); Instant lastLogin = resultSet.getTimestamp(6).toInstant(); - return Optional.of(new PlayerProfile(userId, uuid, name, premium, lastIp, lastLogin)); + return Optional.of(new StoredProfile(userId, uuid, name, premium, lastIp, lastLogin)); } return Optional.empty(); } - public void save(PlayerProfile playerProfile) { + public void save(StoredProfile playerProfile) { try (Connection con = dataSource.getConnection()) { - String uuid = playerProfile.getId().map(UUIDTypeAdapter::toMojangId).orElse(null); + String uuid = playerProfile.getOptId().map(UUIDAdapter::toMojangId).orElse(null); if (playerProfile.isSaved()) { try (PreparedStatement saveStmt = con.prepareStatement(UPDATE_PROFILE)) { saveStmt.setString(1, uuid); - saveStmt.setString(2, playerProfile.getPlayerName()); + saveStmt.setString(2, playerProfile.getName()); saveStmt.setBoolean(3, playerProfile.isPremium()); saveStmt.setString(4, playerProfile.getLastIp()); @@ -158,7 +157,7 @@ public class AuthStorage { try (PreparedStatement saveStmt = con.prepareStatement(INSERT_PROFILE, RETURN_GENERATED_KEYS)) { saveStmt.setString(1, uuid); - saveStmt.setString(2, playerProfile.getPlayerName()); + saveStmt.setString(2, playerProfile.getName()); saveStmt.setBoolean(3, playerProfile.isPremium()); saveStmt.setString(4, playerProfile.getLastIp()); diff --git a/core/src/main/java/com/github/games647/fastlogin/core/CommonUtil.java b/core/src/main/java/com/github/games647/fastlogin/core/CommonUtil.java index d7e51b78..b22a9e9f 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/CommonUtil.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/CommonUtil.java @@ -1,5 +1,6 @@ package com.github.games647.fastlogin.core; +import com.github.games647.craftapi.cache.SafeCacheBuilder; import com.google.common.cache.CacheLoader; import java.lang.reflect.Constructor; @@ -17,7 +18,7 @@ public class CommonUtil { private static final char TRANSLATED_CHAR = 'ยง'; public static ConcurrentMap buildCache(int expireAfterWrite, int maxSize) { - CompatibleCacheBuilder builder = CompatibleCacheBuilder.newBuilder(); + SafeCacheBuilder builder = SafeCacheBuilder.newBuilder(); if (expireAfterWrite > 0) { builder.expireAfterWrite(expireAfterWrite, TimeUnit.MINUTES); diff --git a/core/src/main/java/com/github/games647/fastlogin/core/CompatibleCacheBuilder.java b/core/src/main/java/com/github/games647/fastlogin/core/CompatibleCacheBuilder.java deleted file mode 100644 index c3b2ae46..00000000 --- a/core/src/main/java/com/github/games647/fastlogin/core/CompatibleCacheBuilder.java +++ /dev/null @@ -1,312 +0,0 @@ -package com.github.games647.fastlogin.core; - -import com.google.common.base.Ticker; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.RemovalListener; - -import java.lang.reflect.Method; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; - -/** - * Represents a Guava CacheBuilder that is compatible with both Guava 10 (Minecraft 1.7.X) and 13 - */ -public class CompatibleCacheBuilder { - - private static Method BUILD_METHOD; - private static Method AS_MAP_METHOD; - - /** - * Construct a new safe cache builder. - * - * @param Key type - * @param Value type - * @return A new cache builder. - */ - public static CompatibleCacheBuilder newBuilder() { - return new CompatibleCacheBuilder<>(); - } - - private final CacheBuilder builder; - - @SuppressWarnings("unchecked") - private CompatibleCacheBuilder() { - builder = (CacheBuilder) CacheBuilder.newBuilder(); - } - - /** - * Guides the allowed concurrency among update operations. Used as a hint for internal sizing. The table is - * internally partitioned to try to permit the indicated number of concurrent updates without contention. Because - * assignment of entries to these partitions is not necessarily uniform, the actual concurrency observed may vary. - * Ideally, you should choose a value to accommodate as many threads as will ever concurrently modify the table. - * Using a significantly higher value than you need can waste space and time, and a significantly lower value can - * lead to thread contention. But overestimates and underestimates within an order of magnitude do not usually have - * much noticeable impact. A value of one permits only one thread to modify the cache at a time, but since read - * operations can proceed concurrently, this still yields higher concurrency than full synchronization. Defaults to - * 4. - *

- *

- * Note:The default may change in the future. If you care about this value, you should always choose it - * explicitly. - * - * @param concurrencyLevel New concurrency level - * @return This for chaining - * @throws IllegalArgumentException if {@code concurrencyLevel} is non-positive - * @throws IllegalStateException if a concurrency level was already set - */ - public CompatibleCacheBuilder concurrencyLevel(int concurrencyLevel) { - builder.concurrencyLevel(concurrencyLevel); - return this; - } - - /** - * Specifies that each entry should be automatically removed from the cache once a fixed duration has elapsed after - * the entry's creation, or last access. Access time is reset by - * {@link com.google.common.cache.Cache#get Cache.get()}, but not by operations on the view returned by - * {@link com.google.common.cache.Cache#asMap() Cache.asMap()}. - *

- *

- * When {@code duration} is zero, elements will be evicted immediately after being loaded into the cache. This has - * the same effect as invoking {@link #maximumSize maximumSize}{@code (0)}. It can be useful in testing, or to - * disable caching temporarily without a code change. - *

- *

- * Expired entries may be counted by {@link com.google.common.cache.Cache#size Cache.size()}, but will never be - * visible to read or write operations. Expired entries are currently cleaned up during write operations, or during - * occasional read operations in the absence of writes; though this behavior may change in the future. - * - * @param duration the length of time after an entry is last accessed that it should be automatically removed - * @param unit the unit that {@code duration} is expressed in - * @return This for chaining - * @throws IllegalArgumentException if {@code duration} is negative - * @throws IllegalStateException if the time to idle or time to live was already set - */ - public CompatibleCacheBuilder expireAfterAccess(long duration, TimeUnit unit) { - builder.expireAfterAccess(duration, unit); - return this; - } - - /** - * Specifies that each entry should be automatically removed from the cache once a fixed duration has elapsed after - * the entry's creation, or the most recent replacement of its value. - *

- *

- * When {@code duration} is zero, elements will be evicted immediately after being loaded into the cache. This has - * the same effect as invoking {@link #maximumSize maximumSize}{@code (0)}. It can be useful in testing, or to - * disable caching temporarily without a code change. - *

- *

- * Expired entries may be counted by {@link com.google.common.cache.Cache#size Cache.size()}, but will never be - * visible to read or write operations. Expired entries are currently cleaned up during write operations, or during - * occasional read operations in the absence of writes; though this behavior may change in the future. - * - * @param duration the length of time after an entry is created that it should be automatically removed - * @param unit the unit that {@code duration} is expressed in - * @return This for chaining - * @throws IllegalArgumentException if {@code duration} is negative - * @throws IllegalStateException if the time to live or time to idle was already set - */ - public CompatibleCacheBuilder expireAfterWrite(long duration, TimeUnit unit) { - builder.expireAfterWrite(duration, unit); - return this; - } - - /** - * Sets the minimum total size for the internal hash tables. For example, if the initial capacity is {@code 60}, and - * the concurrency level is {@code 8}, then eight segments are created, each having a hash table of size eight. - * Providing a large enough estimate at construction time avoids the need for expensive resizing operations later, - * but setting this value unnecessarily high wastes memory. - * - * @param initialCapacity - initial capacity - * @return This for chaining - * @throws IllegalArgumentException if {@code initialCapacity} is negative - * @throws IllegalStateException if an initial capacity was already set - */ - public CompatibleCacheBuilder initialCapacity(int initialCapacity) { - builder.initialCapacity(initialCapacity); - return this; - } - - /** - * Specifies the maximum number of entries the cache may contain. Note that the cache may evict an entry before - * this limit is exceeded. As the cache size grows close to the maximum, the cache evicts entries that are less - * likely to be used again. For example, the cache may evict an entry because it hasn't been used recently or very - * often. - *

- *

- * When {@code size} is zero, elements will be evicted immediately after being loaded into the cache. This has the - * same effect as invoking {@link #expireAfterWrite expireAfterWrite}{@code (0, unit)} or - * {@link #expireAfterAccess expireAfterAccess}{@code (0,unit)}. - * It can be useful in testing, or to disable caching temporarily without a code change. - * - * @param size the maximum size of the cache - * @return This for chaining - * @throws IllegalArgumentException if {@code size} is negative - * @throws IllegalStateException if a maximum size was already set - */ - public CompatibleCacheBuilder maximumSize(int size) { - builder.maximumSize(size); - return this; - } - - /** - * Specifies a listener instance, which all caches built using this {@code CacheBuilder} will notify each time an - * entry is removed from the cache by any means. - *

- *

- * Each cache built by this {@code CacheBuilder} after this method is called invokes the supplied listener after - * removing an element for any reason (see removal causes in - * {@link com.google.common.cache.RemovalCause RemovalCause}). It will invoke the listener during invocations of any - * of that cache's public methods (even read-only methods). - *

- *

- * Important note: Instead of returning this as a {@code CacheBuilder} instance, this method returns - * {@code CacheBuilder}. From this point on, either the original reference or the returned reference may be - * used to complete configuration and build the cache, but only the "generic" one is type-safe. That is, it will - * properly prevent you from building caches whose key or value types are incompatible with the types accepted by - * the listener already provided; the {@code CacheBuilder} type cannot do this. For best results, simply use the - * standard method-chaining idiom, as illustrated in the documentation at top, configuring a {@code CacheBuilder} - * and building your {@link com.google.common.cache.Cache Cache} all in a single statement. - *

- *

- * Warning: if you ignore the above advice, and use this {@code CacheBuilder} to build a cache whose key or - * value type is incompatible with the listener, you will likely experience a {@link ClassCastException} at some - * undefined point in the future. - * - * @param Key type - * @param Value type - * @param listener - removal listener - * @return This for chaining - * @throws IllegalStateException if a removal listener was already set - */ - @SuppressWarnings("unchecked") - public CompatibleCacheBuilder removalListener( - RemovalListener listener) { - builder.removalListener(listener); - return (CompatibleCacheBuilder) this; - } - - /** - * Specifies a nanosecond-precision time source for use in determining when entries should be expired. By default, - * {@link System#nanoTime} is used. - *

- *

- * The primary intent of this method is to facilitate testing of caches which have been configured with - * {@link #expireAfterWrite} or {@link #expireAfterAccess}. - * - * @param ticker - ticker - * @return This for chaining - * @throws IllegalStateException if a ticker was already set - */ - public CompatibleCacheBuilder ticker(Ticker ticker) { - builder.ticker(ticker); - return this; - } - - /** - * Specifies that each value (not key) stored in the cache should be wrapped in a - * {@link java.lang.ref.SoftReference SoftReference} (by default, strong references are used). Softly-referenced - * objects will be garbage-collected in a globally - * least-recently-used manner, in response to memory demand. - *

- *

- * Warning: in most circumstances it is better to set a per-cache {@linkplain #maximumSize maximum size} - * instead of using soft references. You should only use this method if you are well familiar with the practical - * consequences of soft references. - *

- *

- * Note: when this method is used, the resulting cache will use identity ({@code ==}) comparison to determine - * equality of values. - * - * @return This for chaining - * @throws IllegalStateException if the value strength was already set - */ - public CompatibleCacheBuilder softValues() { - builder.softValues(); - return this; - } - - /** - * Specifies that each key (not value) stored in the cache should be wrapped in a - * {@link java.lang.ref.WeakReference WeakReference} (by default, strong references are used). - *

- *

- * Warning: when this method is used, the resulting cache will use identity ({@code ==}) comparison to - * determine equality of keys. - * - * @return This for chaining - * @throws IllegalStateException if the key strength was already set - */ - public CompatibleCacheBuilder weakKeys() { - builder.weakKeys(); - return this; - } - - /** - * Specifies that each value (not key) stored in the cache should be wrapped in a - * {@link java.lang.ref.WeakReference WeakReference} (by default, strong references are used). - *

- *

- * Weak values will be garbage collected once they are weakly reachable. This makes them a poor candidate for - * caching; consider {@link #softValues} instead. - *

- *

- * Note: when this method is used, the resulting cache will use identity ({@code ==}) comparison to determine - * equality of values. - * - * @return This for chaining - * @throws IllegalStateException if the value strength was already set - */ - public CompatibleCacheBuilder weakValues() { - builder.weakValues(); - return this; - } - - /** - * Returns the cache wrapped as a ConcurrentMap. - *

- * We can't return the direct Cache instance as it changed in Guava 13. - * - * @param Key type - * @param Value type - * @param loader - cache loader - * @return The cache as a a map. - */ - @SuppressWarnings("unchecked") - public ConcurrentMap build(CacheLoader loader) { - Object cache; - - if (BUILD_METHOD == null) { - try { - BUILD_METHOD = builder.getClass().getDeclaredMethod("build", CacheLoader.class); - BUILD_METHOD.setAccessible(true); - } catch (Exception e) { - throw new IllegalStateException("Unable to find CacheBuilder.build(CacheLoader)", e); - } - } - - // Attempt to build the Cache - try { - cache = BUILD_METHOD.invoke(builder, loader); - } catch (Exception e) { - throw new IllegalStateException("Unable to invoke " + BUILD_METHOD + " on " + builder, e); - } - - if (AS_MAP_METHOD == null) { - try { - AS_MAP_METHOD = cache.getClass().getMethod("asMap"); - AS_MAP_METHOD.setAccessible(true); - } catch (Exception e) { - throw new IllegalStateException("Unable to find Cache.asMap() in " + cache, e); - } - } - - // Retrieve it as a map - try { - return (ConcurrentMap) AS_MAP_METHOD.invoke(cache); - } catch (Exception e) { - throw new IllegalStateException("Unable to invoke " + AS_MAP_METHOD + " on " + cache, e); - } - } -} diff --git a/core/src/main/java/com/github/games647/fastlogin/core/PlayerProfile.java b/core/src/main/java/com/github/games647/fastlogin/core/StoredProfile.java similarity index 70% rename from core/src/main/java/com/github/games647/fastlogin/core/PlayerProfile.java rename to core/src/main/java/com/github/games647/fastlogin/core/StoredProfile.java index 73042789..7a7a6dcc 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/PlayerProfile.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/StoredProfile.java @@ -1,29 +1,31 @@ package com.github.games647.fastlogin.core; +import com.github.games647.craftapi.model.Profile; + import java.time.Instant; import java.util.Optional; import java.util.UUID; -public class PlayerProfile { +import javax.annotation.Nullable; + +public class StoredProfile extends Profile { - private String playerName; private long rowId; - private UUID uuid; private boolean premium; private String lastIp; private Instant lastLogin; - public PlayerProfile(long rowId, UUID uuid, String playerName, boolean premium, String lastIp, Instant lastLogin) { + public StoredProfile(long rowId, UUID uuid, String playerName, boolean premium, String lastIp, Instant lastLogin) { + super(uuid, playerName); + this.rowId = rowId; - this.uuid = uuid; - this.playerName = playerName; this.premium = premium; this.lastIp = lastIp; this.lastLogin = lastLogin; } - public PlayerProfile(UUID uuid, String playerName, boolean premium, String lastIp) { + public StoredProfile(UUID uuid, String playerName, boolean premium, String lastIp) { this(-1, uuid, playerName, premium, lastIp, Instant.now()); } @@ -31,12 +33,8 @@ public class PlayerProfile { return rowId >= 0; } - public synchronized String getPlayerName() { - return playerName; - } - public synchronized void setPlayerName(String playerName) { - this.playerName = playerName; + this.name = playerName; } public synchronized long getRowId() { @@ -47,12 +45,17 @@ public class PlayerProfile { this.rowId = generatedId; } - public synchronized Optional getId() { - return Optional.ofNullable(uuid); + @Nullable + public synchronized UUID getId() { + return id; } - public synchronized void setId(UUID uuid) { - this.uuid = uuid; + public synchronized Optional getOptId() { + return Optional.ofNullable(id); + } + + public synchronized void setId(UUID uniqueId) { + this.id = uniqueId; } public synchronized boolean isPremium() { @@ -82,12 +85,10 @@ public class PlayerProfile { @Override public synchronized String toString() { return this.getClass().getSimpleName() + '{' + - "playerName='" + playerName + '\'' + - ", rowId=" + rowId + - ", uuid=" + uuid + + "rowId=" + rowId + ", premium=" + premium + ", lastIp='" + lastIp + '\'' + ", lastLogin=" + lastLogin + - '}'; + "} " + super.toString(); } } diff --git a/core/src/main/java/com/github/games647/fastlogin/core/mojang/BalancedSSLFactory.java b/core/src/main/java/com/github/games647/fastlogin/core/mojang/BalancedSSLFactory.java deleted file mode 100644 index f76d6ce0..00000000 --- a/core/src/main/java/com/github/games647/fastlogin/core/mojang/BalancedSSLFactory.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.github.games647.fastlogin.core.mojang; - -import com.google.common.collect.Iterables; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.Socket; -import java.util.Iterator; - -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLSocketFactory; - -public class BalancedSSLFactory extends SSLSocketFactory { - - //in order to be thread-safe - private final Iterator iterator; - private final SSLSocketFactory oldFactory; - - public BalancedSSLFactory(SSLSocketFactory oldFactory, Iterable localAddresses) { - this.oldFactory = oldFactory; - this.iterator = Iterables.cycle(localAddresses).iterator(); - } - - public BalancedSSLFactory(Iterable iterator) { - this(HttpsURLConnection.getDefaultSSLSocketFactory(), iterator); - } - - @Override - public String[] getDefaultCipherSuites() { - return oldFactory.getDefaultCipherSuites(); - } - - @Override - public String[] getSupportedCipherSuites() { - return oldFactory.getSupportedCipherSuites(); - } - - @Override - public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException { - return oldFactory.createSocket(host, port, getNextLocalAddress(), 0); - } - - @Override - public Socket createSocket(String host, int port) throws IOException { - return oldFactory.createSocket(host, port, getNextLocalAddress(), 0); - } - - @Override - public Socket createSocket(String host, int port, InetAddress localAddress, int localPort) - throws IOException { - //default - return oldFactory.createSocket(host, port, localAddress, localPort); - } - - @Override - public Socket createSocket(InetAddress host, int port) throws IOException { - return oldFactory.createSocket(host, port, getNextLocalAddress(), 0); - } - - @Override - public Socket createSocket(InetAddress host, int port, InetAddress local, int localPort) throws IOException { - //Default - return oldFactory.createSocket(host, port, local, localPort); - } - - private InetAddress getNextLocalAddress() { - synchronized (iterator) { - return iterator.next(); - } - } -} diff --git a/core/src/main/java/com/github/games647/fastlogin/core/mojang/GameProfile.java b/core/src/main/java/com/github/games647/fastlogin/core/mojang/GameProfile.java deleted file mode 100644 index 5f03f522..00000000 --- a/core/src/main/java/com/github/games647/fastlogin/core/mojang/GameProfile.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.github.games647.fastlogin.core.mojang; - -import java.util.UUID; - -public class GameProfile { - - private UUID id; - private String name; - - public GameProfile(UUID id, String name) { - this.id = id; - this.name = name; - } - - public GameProfile() { - //gson - } - - public UUID getId() { - return id; - } - - public String getName() { - return name; - } - - @Override - public String toString() { - return this.getClass().getSimpleName() + '{' + - "id=" + id + - ", name='" + name + '\'' + - '}'; - } -} diff --git a/core/src/main/java/com/github/games647/fastlogin/core/mojang/MojangApiConnector.java b/core/src/main/java/com/github/games647/fastlogin/core/mojang/MojangApiConnector.java index e6257245..e69de29b 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/mojang/MojangApiConnector.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/mojang/MojangApiConnector.java @@ -1,176 +0,0 @@ -package com.github.games647.fastlogin.core.mojang; - -import com.github.games647.fastlogin.core.CommonUtil; -import com.github.games647.fastlogin.core.shared.LoginSession; -import com.google.common.collect.Iterables; -import com.google.common.net.HostAndPort; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Proxy; -import java.net.Proxy.Type; -import java.net.URL; -import java.net.UnknownHostException; -import java.time.Duration; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLSocketFactory; - -import org.slf4j.Logger; - -public class MojangApiConnector { - - //http connection, read timeout and user agent for a connection to mojang api servers - private static final int TIMEOUT = 3 * 1_000; - private static final String USER_AGENT = "Premium-Checker"; - private static final int RATE_LIMIT_CODE = 429; - - //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_ - //compile the pattern only on plugin enable -> and this have to be thread-safe - private final Pattern validNameMatcher = Pattern.compile("^\\w{2,16}$"); - - private final Iterator proxies; - private final SSLSocketFactory sslFactory; - private final Map requests = CommonUtil.buildCache(10, -1); - private final int rateLimit; - - private Instant lastRateLimit = Instant.now().minus(10, ChronoUnit.MINUTES); - - protected final Logger logger; - protected final Gson gson = new GsonBuilder() - .registerTypeAdapter(UUID.class, new UUIDTypeAdapter()).create(); - - public MojangApiConnector(Logger logger, Iterable localAddresses, int rateLimit - , Iterable proxies) { - this.logger = logger; - this.rateLimit = Math.max(rateLimit, 600); - this.sslFactory = buildAddresses(logger, localAddresses); - - Collection proxyBuilder = new ArrayList<>(); - for (HostAndPort proxy : proxies) { - proxyBuilder.add(new Proxy(Type.HTTP, new InetSocketAddress(proxy.getHostText(), proxy.getPort()))); - } - - this.proxies = Iterables.cycle(proxyBuilder).iterator(); - } - - public Optional getPremiumUUID(String playerName) { - if (!validNameMatcher.matcher(playerName).matches()) { - //check if it's a valid player name - return Optional.empty(); - } - - try { - HttpsURLConnection connection; - if (requests.size() >= rateLimit || Duration.between(lastRateLimit, Instant.now()).getSeconds() < 60 * 10) { - synchronized (proxies) { - if (proxies.hasNext()) { - connection = getConnection(UUID_LINK + playerName, proxies.next()); - } else { - return Optional.empty(); - } - } - } else { - requests.put(new Object(), new Object()); - connection = getConnection(UUID_LINK + playerName); - } - - if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { - String content = reader.lines().collect(Collectors.joining()); - return Optional.of(getUUIDFromJson(content)); - } - } else if (connection.getResponseCode() == RATE_LIMIT_CODE) { - logger.info("Mojang's rate-limit reached. The public IPv4 address of this server issued more than 600" + - " Name -> UUID requests within 10 minutes. Once those 10 minutes ended we could make requests" + - " again. In the meanwhile new skins can only be downloaded using the UUID directly." + - " If you are using BungeeCord, consider adding a caching server in order to prevent multiple" + - " Spigot servers creating the same requests against Mojang's servers."); - lastRateLimit = Instant.now(); - if (!connection.usingProxy()) { - return getPremiumUUID(playerName); - } - } - //204 - no content for not found - } catch (Exception ex) { - logger.error("Failed to check if player has a paid account", ex); - } - - return Optional.empty(); - } - - public boolean hasJoinedServer(LoginSession session, String serverId, InetSocketAddress ip) { - //only available in Spigot and not in BungeeCord - return false; - } - - private UUID getUUIDFromJson(String json) { - boolean isArray = json.startsWith("["); - - GameProfile mojangPlayer; - if (isArray) { - mojangPlayer = gson.fromJson(json, GameProfile[].class)[0]; - } else { - mojangPlayer = gson.fromJson(json, GameProfile.class); - } - - return mojangPlayer.getId(); - } - - protected HttpsURLConnection getConnection(String url, Proxy proxy) throws IOException { - HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection(proxy); - connection.setConnectTimeout(TIMEOUT); - connection.setReadTimeout(2 * TIMEOUT); - //the new Mojang API just uses json as response - connection.setRequestProperty("Content-Type", "application/json"); - connection.setRequestProperty("User-Agent", USER_AGENT); - - connection.setSSLSocketFactory(sslFactory); - - //this connection doesn't need to be closed. So can make use of keep alive in java - return connection; - } - - protected HttpsURLConnection getConnection(String url) throws IOException { - return getConnection(url, Proxy.NO_PROXY); - } - - private SSLSocketFactory buildAddresses(Logger logger, Iterable localAddresses) { - Set addresses = new HashSet<>(); - for (String localAddress : localAddresses) { - try { - InetAddress address = InetAddress.getByName(localAddress.replace('-', '.')); - addresses.add(address); - } catch (UnknownHostException ex) { - logger.error("IP-Address is unknown to us", ex); - } - } - - if (addresses.isEmpty()) { - return HttpsURLConnection.getDefaultSSLSocketFactory(); - } - - return new BalancedSSLFactory(HttpsURLConnection.getDefaultSSLSocketFactory(), addresses); - } -} diff --git a/core/src/main/java/com/github/games647/fastlogin/core/mojang/SkinProperties.java b/core/src/main/java/com/github/games647/fastlogin/core/mojang/SkinProperties.java deleted file mode 100644 index 6e97a277..00000000 --- a/core/src/main/java/com/github/games647/fastlogin/core/mojang/SkinProperties.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.github.games647.fastlogin.core.mojang; - -public class SkinProperties { - - public static final String TEXTURE_KEY = "textures"; - - private final String name = TEXTURE_KEY; - - private String value; - private String signature; - - public String getValue() { - return value; - } - - public String getSignature() { - return signature; - } - - @Override - public String toString() { - return this.getClass().getSimpleName() + '{' + - "name='" + name + '\'' + - ", value='" + value + '\'' + - ", signature='" + signature + '\'' + - '}'; - } -} diff --git a/core/src/main/java/com/github/games647/fastlogin/core/mojang/UUIDTypeAdapter.java b/core/src/main/java/com/github/games647/fastlogin/core/mojang/UUIDTypeAdapter.java index d85fb301..e69de29b 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/mojang/UUIDTypeAdapter.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/mojang/UUIDTypeAdapter.java @@ -1,36 +0,0 @@ -package com.github.games647.fastlogin.core.mojang; - -import com.google.gson.TypeAdapter; -import com.google.gson.internal.bind.TypeAdapters; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.UUID; -import java.util.regex.Pattern; - -public class UUIDTypeAdapter extends TypeAdapter { - - private static final Pattern UUID_PATTERN = Pattern.compile("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})"); - - public static UUID parseId(CharSequence withoutDashes) { - return UUID.fromString(UUID_PATTERN.matcher(withoutDashes).replaceAll("$1-$2-$3-$4-$5")); - } - - public static String toMojangId(UUID uuid) { - return uuid.toString().replace("-", ""); - } - - public static UUID getOfflineUUID(String playerName) { - return UUID.nameUUIDFromBytes(("OfflinePlayer:" + playerName).getBytes(StandardCharsets.UTF_8)); - } - - public void write(JsonWriter out, UUID value) throws IOException { - TypeAdapters.STRING.write(out, toMojangId(value)); - } - - public UUID read(JsonReader in) throws IOException { - return parseId(TypeAdapters.STRING.read(in)); - } -} diff --git a/core/src/main/java/com/github/games647/fastlogin/core/mojang/VerificationReply.java b/core/src/main/java/com/github/games647/fastlogin/core/mojang/VerificationReply.java deleted file mode 100644 index 1bd90634..00000000 --- a/core/src/main/java/com/github/games647/fastlogin/core/mojang/VerificationReply.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.github.games647.fastlogin.core.mojang; - -import java.util.Arrays; -import java.util.UUID; - -public class VerificationReply { - - private UUID id; - private String name; - private SkinProperties[] properties; - - public UUID getId() { - return id; - } - - public String getName() { - return name; - } - - public SkinProperties[] getProperties() { - return Arrays.copyOf(properties, properties.length); - } - - @Override - public String toString() { - return this.getClass().getSimpleName() + '{' + - "id=" + id + - ", name='" + name + '\'' + - ", properties=" + Arrays.toString(properties) + - '}'; - } -} diff --git a/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java b/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java index 66797b91..3c345e18 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java @@ -1,22 +1,27 @@ package com.github.games647.fastlogin.core.shared; +import com.github.games647.craftapi.resolver.MojangResolver; +import com.github.games647.craftapi.resolver.http.RotatingProxySelector; import com.github.games647.fastlogin.core.AuthStorage; import com.github.games647.fastlogin.core.CommonUtil; import com.github.games647.fastlogin.core.hooks.AuthPlugin; import com.github.games647.fastlogin.core.hooks.DefaultPasswordGenerator; import com.github.games647.fastlogin.core.hooks.PasswordGenerator; -import com.github.games647.fastlogin.core.mojang.MojangApiConnector; import com.google.common.net.HostAndPort; import java.io.IOException; import java.io.InputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.Proxy.Type; +import java.net.UnknownHostException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; -import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -28,8 +33,8 @@ import net.md_5.bungee.config.YamlConfiguration; import org.slf4j.Logger; import static java.util.function.Function.identity; -import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toMap; +import static java.util.stream.Collectors.toSet; /** * @param

GameProfile class @@ -38,14 +43,14 @@ import static java.util.stream.Collectors.toMap; */ public class FastLoginCore

> { - protected final Map localeMessages = new ConcurrentHashMap<>(); - + private final Map localeMessages = new ConcurrentHashMap<>(); private final ConcurrentMap pendingLogin = CommonUtil.buildCache(5, -1); private final Collection pendingConfirms = new HashSet<>(); private final T plugin; + private final MojangResolver resolver = new MojangResolver(); + private Configuration config; - private MojangApiConnector apiConnector; private AuthStorage storage; private PasswordGenerator

passwordGenerator = new DefaultPasswordGenerator<>(); private AuthPlugin

authPlugin; @@ -76,12 +81,25 @@ public class FastLoginCore

> { plugin.getLog().error("Failed to load yaml files", ioEx); } - List ipAddresses = config.getStringList("ip-addresses"); - int requestLimit = config.getInt("mojang-request-limit"); - List proxyList = config.get("proxies", new ArrayList<>()); - List proxies = proxyList.stream().map(HostAndPort::fromString).collect(toList()); + Set proxies = config.getStringList("proxies") + .stream() + .map(HostAndPort::fromString) + .map(proxy -> new InetSocketAddress(proxy.getHostText(), proxy.getPort())) + .map(sa -> new Proxy(Type.HTTP, sa)) + .collect(toSet()); - this.apiConnector = plugin.makeApiConnector(ipAddresses, requestLimit, proxies); + Collection addresses = new HashSet<>(); + for (String localAddress : config.getStringList("ip-addresses")) { + try { + addresses.add(InetAddress.getByName(localAddress.replace('-', '.'))); + } catch (UnknownHostException ex) { + plugin.getLog().error("IP-Address is unknown to us", ex); + } + } + + resolver.setMaxNameRequests(config.getInt("mojang-request-limit")); + resolver.setProxySelector(new RotatingProxySelector(proxies)); + resolver.setOutgoingAddresses(addresses); } private Configuration loadFile(String fileName) throws IOException { @@ -96,8 +114,8 @@ public class FastLoginCore

> { return configProvider.load(Files.newBufferedReader(file), defaults); } - public MojangApiConnector getApiConnector() { - return apiConnector; + public MojangResolver getResolver() { + return resolver; } public AuthStorage getStorage() { diff --git a/core/src/main/java/com/github/games647/fastlogin/core/shared/ForceLoginManagement.java b/core/src/main/java/com/github/games647/fastlogin/core/shared/ForceLoginManagement.java index 00a9bb58..d2863618 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/shared/ForceLoginManagement.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/shared/ForceLoginManagement.java @@ -1,7 +1,7 @@ package com.github.games647.fastlogin.core.shared; import com.github.games647.fastlogin.core.AuthStorage; -import com.github.games647.fastlogin.core.PlayerProfile; +import com.github.games647.fastlogin.core.StoredProfile; import com.github.games647.fastlogin.core.hooks.AuthPlugin; public abstract class ForceLoginManagement

> @@ -25,7 +25,7 @@ public abstract class ForceLoginManagement

{ } public void onLogin(String username, S source) { - PlayerProfile profile = core.getStorage().loadProfile(username); + StoredProfile profile = core.getStorage().loadProfile(username); if (profile == null) { return; } @@ -44,9 +45,9 @@ public abstract class JoinManagement

{ return; } - Optional premiumUUID = Optional.empty(); + Optional premiumUUID = Optional.empty(); if (config.get("nameChangeCheck", false) || config.get("autoRegister", false)) { - premiumUUID = core.getApiConnector().getPremiumUUID(username); + premiumUUID = core.getResolver().findProfile(username); } if (!premiumUUID.isPresent() @@ -61,12 +62,17 @@ public abstract class JoinManagement

{ startCrackedSession(source, profile, username); } } + } catch (RateLimitException rateLimitEx) { + core.getPlugin().getLog().error("Mojang's rate limit reached for {}. The public IPv4 address of this" + + " server issued more than 600 Name -> UUID requests within 10 minutes. After those 10" + + " minutes we can make requests again.", username); } catch (Exception ex) { + core.getPlugin().getLog().error("Failed to check premium state for {}", username, ex); core.getPlugin().getLog().error("Failed to check premium state of {}", username, ex); } } - private boolean checkPremiumName(S source, String username, PlayerProfile profile) throws Exception { + private boolean checkPremiumName(S source, String username, StoredProfile profile) throws Exception { core.getPlugin().getLog().info("GameProfile {} uses a premium username", username); if (core.getConfig().get("autoRegister", false) && (authHook == null || !authHook.isRegistered(username))) { requestPremiumLogin(source, profile, username, false); @@ -76,18 +82,18 @@ public abstract class JoinManagement

{ return false; } - private boolean checkNameChange(S source, String username, UUID premiumUUID) { + private boolean checkNameChange(S source, String username, Profile profile) { //user not exists in the db if (core.getConfig().get("nameChangeCheck", false)) { - PlayerProfile profile = core.getStorage().loadProfile(premiumUUID); - if (profile != null) { + StoredProfile storedProfile = core.getStorage().loadProfile(profile.getId()); + if (storedProfile != null) { //uuid exists in the database - core.getPlugin().getLog().info("GameProfile {} changed it's username", premiumUUID); + core.getPlugin().getLog().info("GameProfile {} changed it's username", profile); //update the username to the new one in the database - profile.setPlayerName(username); + storedProfile.setPlayerName(username); - requestPremiumLogin(source, profile, username, false); + requestPremiumLogin(source, storedProfile, username, false); return true; } } @@ -95,7 +101,7 @@ public abstract class JoinManagement

{ return false; } - public abstract void requestPremiumLogin(S source, PlayerProfile profile, String username, boolean registered); + public abstract void requestPremiumLogin(S source, StoredProfile profile, String username, boolean registered); - public abstract void startCrackedSession(S source, PlayerProfile profile, String username); + public abstract void startCrackedSession(S source, StoredProfile profile, String username); } diff --git a/core/src/main/java/com/github/games647/fastlogin/core/shared/LoginSession.java b/core/src/main/java/com/github/games647/fastlogin/core/shared/LoginSession.java index b973d3db..015c63aa 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/shared/LoginSession.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/shared/LoginSession.java @@ -1,19 +1,19 @@ package com.github.games647.fastlogin.core.shared; -import com.github.games647.fastlogin.core.PlayerProfile; +import com.github.games647.fastlogin.core.StoredProfile; import java.util.UUID; public abstract class LoginSession { private final String username; - private final PlayerProfile profile; + private final StoredProfile profile; private UUID uuid; protected boolean registered; - public LoginSession(String username, boolean registered, PlayerProfile profile) { + public LoginSession(String username, boolean registered, StoredProfile profile) { this.username = username; this.registered = registered; this.profile = profile; @@ -32,7 +32,7 @@ public abstract class LoginSession { return !registered; } - public PlayerProfile getProfile() { + public StoredProfile getProfile() { return profile; } diff --git a/core/src/main/java/com/github/games647/fastlogin/core/shared/PlatformPlugin.java b/core/src/main/java/com/github/games647/fastlogin/core/shared/PlatformPlugin.java index 71f6938f..2fec3b8d 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/shared/PlatformPlugin.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/shared/PlatformPlugin.java @@ -1,10 +1,6 @@ package com.github.games647.fastlogin.core.shared; -import com.github.games647.fastlogin.core.mojang.MojangApiConnector; -import com.google.common.net.HostAndPort; - import java.nio.file.Path; -import java.util.List; import java.util.concurrent.ThreadFactory; import org.slf4j.Logger; @@ -22,6 +18,4 @@ public interface PlatformPlugin { default ThreadFactory getThreadFactory() { return null; } - - MojangApiConnector makeApiConnector(List addresses, int requests, List proxies); }