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 b7940b61..8323cef1 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 @@ -30,6 +30,14 @@ public class BukkitLoginSession extends LoginSession { this.verifyToken = verifyToken.clone(); } + public BukkitLoginSession(String username, String serverId, byte[] verifyToken, StoredProfile profile) { + // confirmation login + super(username, profile); + + this.serverId = serverId; + this.verifyToken = verifyToken.clone(); + } + //available for BungeeCord public BukkitLoginSession(String username, boolean registered) { this(username, "", EMPTY_ARRAY, registered, null); diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/command/PremiumCommand.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/command/PremiumCommand.java index 28974bae..0742df9f 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/command/PremiumCommand.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/command/PremiumCommand.java @@ -1,10 +1,9 @@ package com.github.games647.fastlogin.bukkit.command; import com.github.games647.fastlogin.bukkit.FastLoginBukkit; +import com.github.games647.fastlogin.core.ConfirmationState; import com.github.games647.fastlogin.core.StoredProfile; -import java.util.UUID; - import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -36,31 +35,44 @@ public class PremiumCommand extends ToggleCommand { return; } - if (forwardPremiumCommand(sender, sender.getName())) { + Player player = (Player) sender; + String playerName = sender.getName(); + if (forwardPremiumCommand(sender, playerName)) { return; } - UUID id = ((Player) sender).getUniqueId(); - if (plugin.getConfig().getBoolean("premium-warning") && !plugin.getCore().getPendingConfirms().contains(id)) { - sender.sendMessage(plugin.getCore().getMessage("premium-warning")); - plugin.getCore().getPendingConfirms().add(id); - return; + // non-bungee mode + if (plugin.getConfig().getBoolean("premium-confirm")) { + ConfirmationState state = plugin.getCore().getPendingConfirms().get(playerName); + if (state == null) { + // no pending confirmation + plugin.getCore().getPendingConfirms().put(playerName, ConfirmationState.REQUIRE_RELOGIN); + player.kickPlayer(plugin.getCore().getMessage("premium-confirm")); + } else if (state == ConfirmationState.REQUIRE_AUTH_PLUGIN_LOGIN) { + // player logged in successful using premium authentication + activate(sender, playerName); } + } else { + activate(sender, playerName); + } + } - plugin.getCore().getPendingConfirms().remove(id); - //todo: load async - StoredProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName()); - if (profile.isPremium()) { - plugin.getCore().sendLocaleMessage("already-exists", sender); - } else { - //todo: resolve uuid - profile.setPremium(true); - plugin.getScheduler().runAsync(() -> { - plugin.getCore().getStorage().save(profile); - }); + private void activate(CommandSender sender, String playerName) { + plugin.getCore().getPendingConfirms().remove(playerName); - plugin.getCore().sendLocaleMessage("add-premium", sender); - } + //todo: load async + StoredProfile profile = plugin.getCore().getStorage().loadProfile(playerName); + if (profile.isPremium()) { + plugin.getCore().sendLocaleMessage("already-exists", sender); + } else { + //todo: resolve uuid + profile.setPremium(true); + plugin.getScheduler().runAsync(() -> { + plugin.getCore().getStorage().save(profile); + }); + + plugin.getCore().sendLocaleMessage("add-premium", sender); + } } private void onPremiumOther(CommandSender sender, Command command, String[] args) { diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/ConnectionListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/ConnectionListener.java index 1b52b49d..74203105 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/ConnectionListener.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/ConnectionListener.java @@ -51,7 +51,6 @@ public class ConnectionListener implements Listener { Player player = quitEvent.getPlayer(); removeBlacklistStatus(player); - plugin.getCore().getPendingConfirms().remove(player.getUniqueId()); plugin.getPremiumPlayers().remove(player.getUniqueId()); } 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 0d46909f..ab6c9953 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 @@ -7,11 +7,11 @@ import com.github.games647.fastlogin.bukkit.FastLoginBukkit; import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPreLoginEvent; import com.github.games647.fastlogin.core.StoredProfile; import com.github.games647.fastlogin.core.shared.JoinManagement; +import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent; import java.security.PublicKey; import java.util.Random; -import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -81,6 +81,26 @@ public class NameCheckTask extends JoinManagement readMessage(forPlayer, channel, data)); } - private void readMessage(ProxiedPlayer forPlayer, String channel, byte[] data) { - FastLoginCore core = plugin.getCore(); - + private void readMessage(ProxiedPlayer fromPlayer, String channel, byte[] data) { ByteArrayDataInput dataInput = ByteStreams.newDataInput(data); if (successChannel.equals(channel)) { - onSuccessMessage(forPlayer); + onSuccessMessage(fromPlayer); } else if (changeChannel.equals(channel)) { ChangePremiumMessage changeMessage = new ChangePremiumMessage(); changeMessage.readFrom(dataInput); String playerName = changeMessage.getPlayerName(); boolean isSourceInvoker = changeMessage.isSourceInvoker(); - if (changeMessage.shouldEnable()) { - if (playerName.equals(forPlayer.getName()) && plugin.getCore().getConfig().get("premium-warning", true) - && !core.getPendingConfirms().contains(forPlayer.getUniqueId())) { - String message = core.getMessage("premium-warning"); - forPlayer.sendMessage(TextComponent.fromLegacyText(message)); - core.getPendingConfirms().add(forPlayer.getUniqueId()); - return; - } - - core.getPendingConfirms().remove(forPlayer.getUniqueId()); - Runnable task = new AsyncToggleMessage(core, forPlayer, playerName, true, isSourceInvoker); - plugin.getScheduler().runAsync(task); - } else { - Runnable task = new AsyncToggleMessage(core, forPlayer, playerName, false, isSourceInvoker); - plugin.getScheduler().runAsync(task); - } + onChangeMessage(fromPlayer, changeMessage.shouldEnable(), playerName, isSourceInvoker); } } + private void onChangeMessage(ProxiedPlayer fromPlayer, boolean shouldEnable, String playerName, boolean isSourceInvoker) { + FastLoginCore core = plugin.getCore(); + if (shouldEnable) { + if (!isSourceInvoker) { + // fromPlayer is not the target player + activePremiumLogin(fromPlayer, playerName, false); + return; + } + + if (plugin.getCore().getConfig().getBoolean("premium-confirm", true)) { + ConfirmationState state = plugin.getCore().getPendingConfirms().get(playerName); + if (state == null) { + // no pending confirmation + core.sendLocaleMessage("premium-confirm", fromPlayer); + core.getPendingConfirms().put(playerName, ConfirmationState.REQUIRE_RELOGIN); + } else if (state == ConfirmationState.REQUIRE_AUTH_PLUGIN_LOGIN) { + // player logged in successful using premium authentication + activePremiumLogin(fromPlayer, playerName, true); + } + } else { + activePremiumLogin(fromPlayer, playerName, true); + } + } else { + Runnable task = new AsyncToggleMessage(core, fromPlayer, playerName, false, isSourceInvoker); + plugin.getScheduler().runAsync(task); + } + } + + private void activePremiumLogin(ProxiedPlayer fromPlayer, String playerName, boolean isSourceInvoker) { + plugin.getCore().getPendingConfirms().remove(playerName); + Runnable task = new AsyncToggleMessage(plugin.getCore(), fromPlayer, playerName, true, isSourceInvoker); + plugin.getScheduler().runAsync(task); + } + private void onSuccessMessage(ProxiedPlayer forPlayer) { if (forPlayer.getPendingConnection().isOnlineMode()) { //bukkit module successfully received and force logged in the user @@ -98,9 +115,12 @@ public class PluginMessageListener implements Listener { loginSession.setRegistered(true); if (!loginSession.isAlreadySaved()) { - playerProfile.setPremium(true); - plugin.getCore().getStorage().save(playerProfile); loginSession.setAlreadySaved(true); + + playerProfile.setId(loginSession.getUuid()); + playerProfile.setPremium(true); + + plugin.getCore().getStorage().save(playerProfile); } } } diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/task/AsyncPremiumCheck.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/task/AsyncPremiumCheck.java index 85ccd7e5..2cf62b2c 100644 --- a/bungee/src/main/java/com/github/games647/fastlogin/bungee/task/AsyncPremiumCheck.java +++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/task/AsyncPremiumCheck.java @@ -60,6 +60,12 @@ public class AsyncPremiumCheck extends JoinManagement> { private final Map localeMessages = new ConcurrentHashMap<>(); private final ConcurrentMap pendingLogin = CommonUtil.buildCache(5, -1); - private final Collection pendingConfirms = new HashSet<>(); + private final ConcurrentMap pendingConfirms = CommonUtil.buildCache(1, 1024); private final T plugin; private final MojangResolver resolver = new MojangResolver(); @@ -204,11 +204,18 @@ public class FastLoginCore

> { this.passwordGenerator = passwordGenerator; } + /** + * @return list of player names that are currently during the login process and might fail and so could be used + * for second attempt logins + */ public ConcurrentMap getPendingLogin() { return pendingLogin; } - public Collection getPendingConfirms() { + /** + * @return list of player names that request onlinemode authentication but are not yet approved + */ + public ConcurrentMap getPendingConfirms() { return pendingConfirms; } 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 b485896a..903106f5 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 @@ -28,10 +28,16 @@ public abstract class ForceLoginManagement

authPlugin = core.getAuthPluginHook(); if (authPlugin == null) { - //maybe only bungeecord plugin + //maybe only bungeecord plugin without any auth plugins on Bungee onForceActionSuccess(session); } else { boolean success = true; diff --git a/core/src/main/java/com/github/games647/fastlogin/core/shared/JoinManagement.java b/core/src/main/java/com/github/games647/fastlogin/core/shared/JoinManagement.java index 2f32778c..7ce89cfe 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/shared/JoinManagement.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/shared/JoinManagement.java @@ -2,6 +2,7 @@ package com.github.games647.fastlogin.core.shared; import com.github.games647.craftapi.model.Profile; import com.github.games647.craftapi.resolver.RateLimitException; +import com.github.games647.fastlogin.core.ConfirmationState; import com.github.games647.fastlogin.core.StoredProfile; import com.github.games647.fastlogin.core.hooks.AuthPlugin; import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent; @@ -37,7 +38,14 @@ public abstract class JoinManagement

{ if (profile.isPremium()) { requestPremiumLogin(source, profile, username, true); } else { - startCrackedSession(source, profile, username); + ConfirmationState confirmationState = core.getPendingConfirms().get(username); + if (confirmationState == ConfirmationState.REQUIRE_RELOGIN) { + core.getPendingConfirms().put(username, ConfirmationState.REQUIRE_AUTH_PLUGIN_LOGIN); + requestPremiumLogin(source, profile, username, true); + } else { + // cracked player, but wants to change to premium + startCrackedSession(source, profile, username); + } } } else { if (core.getPendingLogin().remove(ip + username) != null && config.get("secondAttemptCracked", false)) { @@ -108,5 +116,7 @@ public abstract class JoinManagement

{ public abstract void requestPremiumLogin(S source, StoredProfile profile, String username, boolean registered); + public abstract void requestConfirmationLogin(S source, StoredProfile 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 015c63aa..f7b366b2 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 @@ -13,12 +13,19 @@ public abstract class LoginSession { protected boolean registered; + protected boolean confirmationLogin; + public LoginSession(String username, boolean registered, StoredProfile profile) { this.username = username; this.registered = registered; this.profile = profile; } + public LoginSession(String username, StoredProfile profile) { + this(username, true, profile); + this.confirmationLogin = true; + } + public String getUsername() { return username; } @@ -54,6 +61,10 @@ public abstract class LoginSession { this.uuid = uuid; } + public synchronized boolean isConfirmationPending() { + return confirmationLogin; + } + @Override public synchronized String toString() { return this.getClass().getSimpleName() + '{' + diff --git a/core/src/main/resources/config.yml b/core/src/main/resources/config.yml index cf5a3f79..80fd217f 100644 --- a/core/src/main/resources/config.yml +++ b/core/src/main/resources/config.yml @@ -115,11 +115,11 @@ nameChangeCheck: false # ChangeSkin, SkinRestorer, ... forwardSkin: true -# Displays a warning message that this message SHOULD only be invoked by -# users who actually are the owner of this account. So not by cracked players -# -# If they still want to invoke the command, they have to invoke /premium again -premium-warning: true +# Players have to rejoin to verify that they can join through onlinemode authentication. +# After that they have to confirm again using the /premium command that they are also the owner of the auth plugin +# account. +# If the onlinemode authentication fails or the player waits to long, it will fallback to offlinemode authentication. +premium-confirm: true # If you have autoRegister or nameChangeCheck enabled, you could be rate-limited by Mojang. # The requests of the both options will be only made by FastLogin if the username is unknown to the server diff --git a/core/src/main/resources/messages.yml b/core/src/main/resources/messages.yml index f9b8526d..3bf455b9 100644 --- a/core/src/main/resources/messages.yml +++ b/core/src/main/resources/messages.yml @@ -89,9 +89,9 @@ invalid-requst: '&4Invalid request' # Message if the Bukkit isn't fully started to inject the packets not-started: '&cServer is not fully started yet. Please retry' -# Warning message if a user invoked /premium command -premium-warning: '&c&lWARNING: &6This command should &lonly&6 be invoked if you are the owner of this paid Minecraft account -Type &a/premium&6 again to confirm' +# Premium confirmation message if the player tries to activate the command +premium-confirm: '&6Please relogin and type the command again to apply the changes. +If the request fails or you wait to long, it will fallback to offlinemode.' # ========= Bungee/Waterfall only ================================