auth timeout policy
This commit is contained in:
@@ -2,9 +2,18 @@ package id.dartokloning.lobbysystems;
|
||||
|
||||
public final class AuthConfig {
|
||||
public boolean enabled = true;
|
||||
/**
|
||||
* true: a Mojang-verified premium account is authenticated automatically unless
|
||||
* requireLoginEveryJoin is enabled.
|
||||
* false: premium accounts still use Mojang verification, but must also register
|
||||
* and use a Lobby Systems password.
|
||||
*/
|
||||
public boolean premiumAutoLogin = true;
|
||||
public boolean protectPremiumUsernames = true;
|
||||
public int authenticationTimeoutSeconds = 120;
|
||||
public String registrationTimeoutMessage = "Registration timed out after {seconds} seconds. Please reconnect and create your password.";
|
||||
public String loginTimeoutMessage = "Login timed out after {seconds} seconds. Please reconnect and enter your password.";
|
||||
public String authenticationTimeoutMessage = "Authentication timed out after {seconds} seconds. Please reconnect and try again.";
|
||||
public int maxLoginAttempts = 3;
|
||||
public int minimumPasswordLength = 6;
|
||||
public int pbkdf2Iterations = 210000;
|
||||
|
||||
@@ -4,7 +4,6 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetActionBarTextPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
||||
@@ -114,14 +113,14 @@ public final class AuthManager {
|
||||
}
|
||||
|
||||
// false means premium accounts are satisfied by Mojang authentication.
|
||||
if (s.premium && !config.requireLoginEveryJoin) {
|
||||
if (s.premium && config.premiumAutoLogin && !config.requireLoginEveryJoin) {
|
||||
authenticate(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
// true makes the Lobby Systems password an additional layer for premium
|
||||
// accounts, and local accounts always require it on each connection.
|
||||
if (s.premium && config.requireLoginEveryJoin && s.authenticated) {
|
||||
if (s.premium && (!config.premiumAutoLogin || config.requireLoginEveryJoin) && s.authenticated) {
|
||||
s.authenticated = false;
|
||||
s.joinedAt = System.currentTimeMillis();
|
||||
s.attempts = 0;
|
||||
@@ -129,7 +128,6 @@ public final class AuthManager {
|
||||
if (!s.authenticated) {
|
||||
s.stage = isRegistered(player.getName().getString()) ? Stage.LOGIN : Stage.REGISTER;
|
||||
s.joinedAt = System.currentTimeMillis();
|
||||
s.lastCountdownSecond = -1L;
|
||||
showCurrentDialog(player, s);
|
||||
}
|
||||
}
|
||||
@@ -137,7 +135,7 @@ public final class AuthManager {
|
||||
|
||||
/** Called from the login mixin before the player enters play state. */
|
||||
public LoginRoute resolveLoginRoute(String username) {
|
||||
if (!config.enabled || !config.premiumAutoLogin) return LoginRoute.PREMIUM;
|
||||
if (!config.enabled) return LoginRoute.PREMIUM;
|
||||
PremiumResult result = premiumStatus(username);
|
||||
return switch (result) {
|
||||
case PREMIUM -> LoginRoute.PREMIUM;
|
||||
@@ -227,13 +225,18 @@ public final class AuthManager {
|
||||
for (ServerPlayer player : List.copyOf(server.getPlayerList().getPlayers())) {
|
||||
Session s = sessions.get(player.getUUID());
|
||||
if (s == null || s.authenticated) continue;
|
||||
int timeoutSeconds = s.stage == Stage.RULES ? Math.max(1, rules.timeoutSeconds) : Math.max(1, config.authenticationTimeoutSeconds);
|
||||
int timeoutSeconds = s.stage == Stage.RULES
|
||||
? Math.max(1, rules.timeoutSeconds)
|
||||
: Math.max(1, config.authenticationTimeoutSeconds);
|
||||
long remainingMillis = timeoutSeconds * 1000L - (now - s.joinedAt);
|
||||
if (remainingMillis <= 0L) {
|
||||
String timeoutMessage = s.stage == Stage.RULES
|
||||
? "You did not accept the server rules in time. Please reconnect and try again."
|
||||
: "Authentication timed out. Please reconnect and try again.";
|
||||
player.connection.disconnect(Component.literal(timeoutMessage));
|
||||
String template = switch (s.stage) {
|
||||
case RULES -> rules.timeoutMessage;
|
||||
case REGISTER -> config.registrationTimeoutMessage;
|
||||
case LOGIN -> config.loginTimeoutMessage;
|
||||
default -> config.authenticationTimeoutMessage;
|
||||
};
|
||||
player.connection.disconnect(Component.literal(formatTimeoutMessage(template, timeoutSeconds)));
|
||||
continue;
|
||||
}
|
||||
// Keep the player at the safe Lobby authentication position. World safety is
|
||||
@@ -249,24 +252,6 @@ public final class AuthManager {
|
||||
showCurrentDialog(player, s);
|
||||
}
|
||||
|
||||
// A live countdown is shown in the action bar. Do not reopen the dialog every
|
||||
// few seconds: reopening destroys text currently typed into password fields.
|
||||
long remainingSeconds = Math.max(1L, (remainingMillis + 999L) / 1000L);
|
||||
if (remainingSeconds != s.lastCountdownSecond) {
|
||||
s.lastCountdownSecond = remainingSeconds;
|
||||
String countdownLabel = switch (s.stage) {
|
||||
case RULES -> "Accept the rules within: ";
|
||||
case REGISTER -> "Register within: ";
|
||||
case LOGIN -> "Login within: ";
|
||||
default -> "Time remaining: ";
|
||||
};
|
||||
long minutes = remainingSeconds / 60L;
|
||||
long seconds = remainingSeconds % 60L;
|
||||
player.connection.send(new ClientboundSetActionBarTextPacket(
|
||||
Component.literal(countdownLabel + String.format(Locale.ROOT, "%02d:%02d", minutes, seconds))
|
||||
.withStyle(net.minecraft.ChatFormatting.YELLOW)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,7 +278,6 @@ public final class AuthManager {
|
||||
// are accepted, start a fresh timer for REGISTER/LOGIN instead of carrying over
|
||||
// time spent reading the rules.
|
||||
s.joinedAt = System.currentTimeMillis();
|
||||
s.lastCountdownSecond = -1L;
|
||||
continueAfterRules(player);
|
||||
return 1;
|
||||
}
|
||||
@@ -303,7 +287,7 @@ public final class AuthManager {
|
||||
Session s = sessions.get(player.getUUID());
|
||||
if (s == null || s.authenticated) return failAndReopen(player, "§eYou are already authenticated.", DIALOG_REGISTER);
|
||||
if (s.stage == Stage.RULES) return failAndReopen(player, "§cPlease accept the server rules first.", DIALOG_RULES);
|
||||
if (s.premium && !config.requireLoginEveryJoin) return fail(player, "§ePremium accounts use Mojang authentication and do not need a local password.");
|
||||
if (s.premium && config.premiumAutoLogin && !config.requireLoginEveryJoin) return fail(player, "§ePremium accounts use Mojang authentication and do not need a local password.");
|
||||
|
||||
String name = player.getName().getString();
|
||||
if (!s.premium) {
|
||||
@@ -370,7 +354,7 @@ public final class AuthManager {
|
||||
Session s = sessions.get(player.getUUID());
|
||||
if (s == null) return false;
|
||||
|
||||
if (s.premium && !config.requireLoginEveryJoin) {
|
||||
if (s.premium && config.premiumAutoLogin && !config.requireLoginEveryJoin) {
|
||||
authenticate(player);
|
||||
player.sendSystemMessage(Component.literal("§aLobby Systems: Premium account verified. Auto-login successful."));
|
||||
return false;
|
||||
@@ -561,6 +545,13 @@ public final class AuthManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static String formatTimeoutMessage(String template, int seconds) {
|
||||
String value = (template == null || template.isBlank())
|
||||
? "Authentication timed out after {seconds} seconds. Please reconnect and try again."
|
||||
: template;
|
||||
return value.replace("{seconds}", Integer.toString(seconds));
|
||||
}
|
||||
|
||||
private static String key(String username) { return username.toLowerCase(Locale.ROOT); }
|
||||
|
||||
private void saveConfig() throws Exception {
|
||||
@@ -619,7 +610,6 @@ public final class AuthManager {
|
||||
long lastReminder; // retained for binary/source migration; no longer reopens dialogs
|
||||
boolean dialogPending;
|
||||
long dialogReadyAt;
|
||||
long lastCountdownSecond = -1L;
|
||||
final double x, y, z;
|
||||
final float yaw, pitch;
|
||||
Stage stage = Stage.RULES;
|
||||
|
||||
@@ -7,6 +7,7 @@ public final class RulesConfig {
|
||||
public boolean enabled = true;
|
||||
public int version = 1;
|
||||
public int timeoutSeconds = 60;
|
||||
public String timeoutMessage = "You did not accept the server rules within {seconds} seconds. Please reconnect and try again.";
|
||||
public String title = "Server Rules";
|
||||
public String introduction = "Please read and accept our rules:";
|
||||
public List<String> rules = new ArrayList<>(List.of(
|
||||
|
||||
Reference in New Issue
Block a user