You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Added a standalone :Lettuce Gradle subproject (Libraries/Lettuce) bundling io.lettuce:lettuce-core:7.9.0.RELEASE with a private, relocated copy of Netty 4.2 (xyz.kyngs.librelogin.lib.netty) and Project Reactor (xyz.kyngs.librelogin.lib.reactor) to avoid conflicts with the server's Netty 4.1 runtime while excluding native Netty transport binaries.
Added RedisService to manage the native Lettuce Redis client lifecycle, RESP3 protocol negotiation, decorrelated-jitter auto-reconnection, connection state monitoring, and unique cluster node identification (node-<uuid>).
Added RedisPresenceProvider for cluster-wide online player presence tracking (librelogin:player:<uuid> and librelogin:node:<serverId>:players) backed by atomic Lua scripts (DELETE_IF_OWNER and REFRESH_IF_FREE_OR_OWNER) and pipelined heartbeats so disconnecting or shutting-down nodes never delete presence keys owned by another proxy.
Added RedisPubSubHandler on channel librelogin:sync:events to broadcast cross-proxy user cache invalidations (INVALIDATE_USER) and remote player kicks (REMOTE_KICK) with localized message key resolution (key:<messageKey>) or Base64URL-encoded custom text.
Added a standalone :Adventure Gradle subproject (Libraries/Adventure) bundling and relocating Kyori Adventure 4.26.1 (adventure-api, adventure-text-minimessage, adventure-text-serializer-legacy, adventure-text-serializer-plain, adventure-text-serializer-gson) under xyz.kyngs.librelogin.lib.adventure.
Added ComponentBridge and PlatformAudience to serialize relocated Adventure components with JSONOptions.compatibility() (emitting both legacy camelCase and modern snake_case click/hover event keys) and legacy section-sign formatting with optional 1.16+ RGB hex (§x§r§r§g§g§b§b) support.
Added PaperAdventure, PaperNativeAdventure, and PaperAudience to bridge relocated Adventure components to Paper's native Adventure API on Paper 1.16.5+ or legacy Bukkit/Spigot/BungeeChat APIs on Paper 1.13.2–1.16.4 with runtime capability detection and LinkageError fallbacks.
Added VelocityAudience to bridge relocated Adventure components with Velocity's native Adventure Audience and Title APIs.
Added PBKDF2CryptoProvider (PBKDF2 algorithm identifier, PBKDF2-HMAC-SHA256 with 310,000 iterations and 256-bit derived key length) backed by Password4j.
Added SCryptCryptoProvider (SCrypt algorithm identifier, configured with OWASP parameters N=16384, r=8, p=1, dkLen=32) backed by Password4j.
Added VerifyOnlyCryptoProvider and LegacyMessageDigestCryptoProvider (SHA-256, SHA-512, LOGIT-SHA-256) to verify imported legacy password hashes without allowing them as the default hashing algorithm, automatically re-hashing verified passwords with the default provider upon login.
Added LegacyHashNormalizer to convert legacy split hash/salt database representations (BCrypt-2A/2B/2X/2Y, Argon-2ID) and AuthMe $SHA$<salt>$<hash> strings into standard Modular Crypt Format (MCF) hashes with salt = null.
Added psw4j.properties (global.salt.length=64) to explicitly configure Password4j's default 64-byte salt length.
Added CryptoUtil.hmacSha256(String, String) and AuthenticLibreLogin.anonymizeIP(String) to support HMAC-SHA256 IP address hashing when privacy.ip-storage-mode is set to HASHED, alongside CryptoUtil.wipe(char[]) and CryptoUtil.wipe(byte[]) to zero-fill sensitive arrays in memory.
Added GeneralUtil.isValidEmail(String), GeneralUtil.escapeHtml(String), GeneralUtil.translateLegacyToMiniMessage(String), and GeneralUtil.userAgent(String).
Added /unregister <password> (UnregisterCommand, permission librelogin.command.unregister), gated by allow-player-unregister, allowing authenticated players to verify their password, clear their credentials/2FA/email, and unregister their own account.
Added /cracked [password] [passwordRepeat] (CrackedCommand and AbstractPremiumCommand, permission librelogin.command.cracked), requiring unregistered premium players to set a password before switching to cracked mode so the account cannot be left unclaimed.
Added /2fa disable <password> in TwoFactorCommand allowing authenticated players to disable 2FA after verifying their password, and added manual TOTP secret key chat output (totp.show-info-manual, totp.secret-key) when map projection is unavailable or fails.
Added /librelogin health (librelogin.admin.health) to display platform name and version, JVM heap usage, database connectivity and connector type, and cached user count (getCachedUsersCount()).
Added /librelogin reload (librelogin.admin.reload) to reload config.conf, messages.conf, backend server tables (AuthenticServerHandler.reload()), forbidden password lists (loadForbiddenPasswords()), and ACF command locales in a single command.
Added /librelogin user forget <player> (librelogin.admin.user.forget) to kick the target player if online and permanently delete their user record from the database.
Added LibreLoginSQLMigrateReadProvider (librelogin-migration-mysql, librelogin-migration-postgresql, librelogin-migration-sqlite) supporting multi-dialect column aliases, second-to-millisecond timestamp conversion, and legacy hash normalization when migrating from existing LibreLogin databases.
Added LoginToSQLMigrateReadProvider (loginto-mysql, loginto-sqlite) to migrate player accounts from LoginTo (LoginTo_Players_Accounts), aborting migration if nameless rows or unsupported password hashes are detected.
Added a one-time migration.done marker file in GeneralUtil.checkAndMigrate so configured database migrations run only once and do not overwrite accounts on subsequent server restarts.
Added SQLMigrateReadProvider.getEscapedTableName() to safely quote SQL table identifiers for PostgreSQL ("...") and MySQL/SQLite (`...`).
Added a database connectivity circuit breaker (MAX_CONSECUTIVE_FAILURES = 3) and half-open recovery prober (PROBE_INTERVAL_MILLIS = 5000, connection.isValid(2)) in AuthenticDatabaseConnector, with isStatementError filtering so SQL constraint or syntax errors (SQLState classes 23 and 42) do not trip the connectivity breaker.
Added AuthenticDatabaseConnector.keepaliveFor(long) to compute a HikariCP keepaliveTime scaled to maxLifetime (or disabled when maxLifetime / 2 < 30000 ms).
Added three bounded Caffeine user lookup caches (userByUuidCache, userByNameCache, userByPremiumUuidCache, max 10,000 entries) in LibreLoginSQLDatabaseProvider with dynamic expiry (5 minutes when Redis is active, 30 seconds standalone) and cross-proxy invalidation broadcasts.
Added transactional user archiving (archiveUser) in LibreLoginSQLDatabaseProvider to copy overwritten profile-conflict accounts into librepremium_data_archive with an archived_at timestamp before deleting them from librepremium_data.
Added batched database cleanup methods purgeInactiveUsers(int days) and purgeAbandonedUsers(int days) (deleting in batches of 500) in LibreLoginSQLDatabaseProvider, scheduled at startup in AuthenticLibreLogin to purge inactive accounts (privacy.auto-purge-inactive-days) and unregistered pre-login reservations older than 7 days.
Added database schema indexes ip_index on librepremium_data(ip), last_seen_index on librepremium_data(last_seen), and last_nickname_lower_index on LOWER(last_nickname) (for PostgreSQL and SQLite), plus automatic case-insensitive collation detection (detectCaseInsensitiveNickname) in LibreLoginMySQLDatabaseProvider.
Added ConfigurationValidator to validate config.conf on load and reload, checking default-crypto-provider (rejecting unknown or verify-only providers), new-uuid-creator, profile-conflict-resolution-strategy, non-empty allowed-commands-while-unauthorized, numeric bounds, port ranges, and redis.key-ttl-seconds > redis.heartbeat-interval-seconds.
Added new message keys in MessageKeys covering database/internal/session/rate-limit/remote/shutdown kicks (kick.*), command/2FA/email/lockout errors (error.*), health diagnostics (health.*), manual TOTP setup (totp.show-info-manual, totp.secret-key, totp.already-enabled, totp.unavailable), localized /librelogin user info values (info.value-na, info.value-enabled, info.value-disabled, info.alts-more, info.2fa-disabled, info.user-forgotten, info.kick-unknown-reason), test email templates (email.test-subject, email.test-content), and updated command syntax/autocomplete entries.
Added pre-login per-IP connection rate limiting (preLoginAttempts, max 8 attempts per 10-second window per non-loopback IP) and zero-allocation character-by-character username validation (isValidMinecraftUsername) in AuthenticListeners.
Added per-IP failed login tracking (loginTriesByIp) and exponentially escalating per-account lockouts (lockouts, 30-second base duration doubling up to 15 minutes) in LoginTryListener.
Added TOTP time-slot replay protection (usedTimesteps cache enforcing strictly increasing time slots per secret) and a 5-minute QR image cache (qrCache) in AuthenticTOTPProvider.
Added ShutdownException and startup lockdown mode in PaperBootstrap (LockdownListener) and VelocityBootstrap so fatal initialization errors deny all logins with an unavailability message instead of calling System.exit or leaving the server open without authentication.
Added runtime-selected Paper listeners ModernPaperChatBlocker (AsyncChatEvent) / LegacyPaperChatBlocker (AsyncPlayerChatEvent) and ModernSpawnLocationListener (AsyncPlayerSpawnLocationEvent) / LegacySpawnLocationListener (PlayerSpawnLocationEvent) for Paper 1.13.2–26.3 compatibility.
Added multi-version reflection and compatibility helpers in PaperUtil (getPluginVersion, getPluginAuthors, 5-tier isProxyEnabled, setBooleanGameRule, createProfileSafe, setInvisible with invisibility potion fallback, hasChangedPosition, isPluginRespawn, and kickPlayerSafe).
Added 7 additional limbo event blockers in paper/Blockers.java: EntityDamageByEntityEvent (when the damager is in limbo), InventoryDragEvent, EntityPickupItemEvent, PlayerItemConsumeEvent, PlayerSwapHandItemsEvent, PlayerInteractEntityEvent, and VehicleEnterEvent.
Added JULLogger backed by java.util.logging.Logger for platforms without SLF4J, SLF4J-style parameterized {} logging methods (info, warn, error, debug) and isDebugEnabled() in Logger and SLF4JLogger, ReadDatabaseProvider.connected(), ServerHandler.getLobbyServerMap(), EventCancelledException constructors, WrongPasswordEvent.AuthenticationSource.UNREGISTER and TFA_DISABLE, @Nullable annotations in LibreLoginPlugin, @FunctionalInterface on ThrowableConsumer, ThrowableFunction, and CancellableTask, and comprehensive English Javadocs across API and plugin classes.
Added ModrinthUpdateChecker querying the Modrinth v2 API (https://api.modrinth.com/v2/project/librelogin-next/version) with semantic version comparison and release channel reporting.
Added a 10-class JUnit 6 test suite in :Plugin: JULLoggerTest, ConfigurationValidatorTest, LegacyHashesTest, KeepaliveTest, MapDataPacketTest, LogFilterTest, TotpReplayTest, ComponentBridgeTest, HardeningTest, and ModrinthUpdateCheckerTest.
Fixes / Changes
Upgraded Gradle from 8.14.5 to 9.8.1 with SHA-256 wrapper verification, enabled org.gradle.configuration-cache, org.gradle.caching, and org.gradle.parallel in gradle.properties, and configured a Java 25 toolchain emitting Java 17 bytecode (--release 17, TargetJvmVersion = 17 on runtime classpaths and 25 on compile-only classpaths) across all subprojects.
Renamed the project, shaded plugin artifact (LibreLoginNext-<version>.jar), API artifact (LibreLoginNext-API-<version>.jar), Velocity @Plugin display name, HikariCP connection pools (LibreLogin Next MySQL/PostgreSQL/SQLite Pool), default totp.label and mail.sender (LibreLogin Next Network), outbound HTTP User-Agent (LibreLogin Next v<version>), and /librelogin about branding to LibreLogin Next, added "samupro-dev" to plugin metadata authors, and quoted api-version: "1.13" in plugin.yml.
Replaced runtime dependency downloading via xyz.kyngs.libby with build-time shaded dependencies using com.gradleup.shadow:9.6.1, mergeServiceFiles(), and relocations under xyz.kyngs.librelogin.lib.*, centralized repository rules with RepositoriesMode.FAIL_ON_PROJECT_REPOS and exclusiveContent in settings.gradle.kts, replaced net.kyori.blossom with a configuration-cache-safe generateVelocityBootstrapCopy task, packaged LICENSE.txt into both :Plugin and :API jars, and excluded config.conf and messages.conf from static resource packaging since both are generated at runtime from configuration keys.
Changed com.github.retrooper:packetevents-spigot (2.14.0) from a bundled shaded library to a required external server plugin in plugin.yml and paper-plugin.yml, and updated PaperLibreLogin to register and unregister only its own PacketListenerCommon without initializing or terminating the global PacketEvents API.
Updated dependencies: com.velocitypowered:velocity-api (3.4.0-SNAPSHOT -> 4.1.0-SNAPSHOT), com.zaxxer:HikariCP (6.3.2 -> 7.1.0), org.mariadb.jdbc:mariadb-java-client (3.5.4 -> 3.5.10), org.postgresql:postgresql (42.7.8 -> 42.7.14), org.xerial:sqlite-jdbc (3.50.3.0 -> 3.53.4.0), co.aikar:acf-velocity & acf-paper (0.5.1-SNAPSHOT), com.github.ben-manes.caffeine:caffeine (3.2.2 -> 3.3.1), org.geysermc.floodgate:api (2.2.0-SNAPSHOT -> 2.2.5-SNAPSHOT), org.apache.logging.log4j:log4j-core (2.25.1 -> 2.26.1), org.jetbrains:annotations (26.1.0), com.diffplug.spotless (7.2.1 -> 8.10.4), and org.junit:junit-bom (5.13.4 -> 6.1.3), while replacing at.favre.lib:bcrypt and org.bouncycastle:bcprov-jdk18on with com.password4j:password4j:1.8.4 and dev.samstevens.totp with com.helger:ph-totp:2.1.1, commons-codec:1.23.0, and com.google.zxing:3.5.4, and changing com.google.guava:guava in :Plugin to compileOnly.
Breaking API change: PlatformHandle, Messages, and PlayerBasedEvent now expose the relocated xyz.kyngs.librelogin.lib.adventure types instead of net.kyori.adventure.
Reworked AuthenticLibreLogin thread management into two bounded ThreadPoolExecutor pools (EXECUTOR with Math.max(8, cpus * 4) threads and 2,000-task queue for post-auth/commands; LOGIN_EXECUTOR with Math.max(4, cpus * 2) threads and 512-task queue for pre-auth handshakes and Mojang lookups) with a shuttingDown guard to reject late tasks during shutdown.
Changed AuthenticLibreLogin.cancelOnExit from an unsynchronized Multimap<P, CancellableTask> keyed by player object to a synchronized SetMultimap<UUID, CancellableTask> keyed by UUID, and added cancelTasksForPlayer(UUID).
Fixed AuthenticLibreLogin.enable() so AuthenticTOTPProvider is initialized whenever totp.enabled is true instead of being disabled when imageProjector is null.
Reworked AuthenticLibreLogin.loadForbiddenPasswords() to preserve the local forbidden-passwords.txt (seeded from forbidden-passwords-template.txt) while asynchronously downloading the remote password list (forbidden-passwords-remote.txt, capped at 16 MB with atomic .tmp move and up to 5 retries) and merging both into an immutable Set.
Updated AuthenticLibreLogin.validPassword(String) to enforce minimum-password-length and compile password-regex once per configuration change while capping regex input evaluation at 128 characters to prevent ReDoS.
Updated AuthenticLibreLogin.disable() and platform shutdown hooks to unregister ACF commands, remove installed LogFilter instances, shut down worker, login, and ping thread pools, close RedisService, disconnect the database connector, and synchronously persist online players' last servers on Paper (saveLastServersNow).
Fixed AuthenticLibreLogin.onExit(P) to verify getPlayerForUUID(uuid) is not a newer reconnected session before cleaning up state, unregister local and Redis presence, and save lastServer asynchronously.
Reworked Argon2IDCryptoProvider and BCrypt2ACryptoProvider to use Password4j and store full MCF strings in HashedPassword.hash() with salt = null, added a CPU-scaled Semaphore (Math.max(2, availableProcessors())) in Argon2IDCryptoProvider to bound concurrent 16 MiB Argon2id evaluations, and added a 72-byte UTF-8 length check in BCrypt2ACryptoProvider.createHash so passwords over 72 bytes fail with error.password-too-long instead of silently truncating.
Fixed LegacyMessageDigestCryptoProvider to instantiate a new MessageDigest per computation instead of sharing a non-thread-safe instance across threads, compare hashes in constant time via MessageDigest.isEqual, and fall back to Charset.defaultCharset() when verifying legacy non-UTF-8 hashes.
Updated HashedPassword.toString() to redact hash and salt as '[PROTECTED]' to prevent credential leakage in logs and stack traces.
Reworked AuthenticAuthorizationProvider to key unAuthorized and awaiting2FA by UUID instead of player reference P, added explicit authorized and authorizing UUID sets, and changed isAuthorized(P) to fail closed (authorized.contains(uuid) && !unAuthorized.containsKey(uuid)).
Updated AuthenticAuthorizationProvider.authorize(...) to use a non-blocking per-UUID gate (authorizing.add(uuid)) instead of interned-string monitors, persist lastAuthentication and anonymized IP to the database via updateUser(user)before granting in-memory authorization, kick other proxy sessions via Redis Pub/Sub after authentication, and run onExit if the player disconnected mid-authorization.
Fixed AuthenticAuthorizationProvider.sendInitialPrompt to poll up to 10 seconds (40 attempts at 250 ms intervals) waiting for getPlayersServerName(player) != null before sending the initial login/register prompt and title on Velocity, and retained emailConfirmCache and passwordResetCache across disconnects until their 10-minute TTL expires.
Centralized session validation in AuthenticListeners.hasValidSession, requiring a positive session-timeout, a non-null lastAuthentication, a constant-time IP match (MessageDigest.isEqual on anonymized IPs), and user.getSecret() == null so 2FA-enabled accounts cannot bypass 2FA via IP session auto-login.
Updated AuthenticListeners.onPreLogin to fail closed with kick.database-error when !databaseProvider.connected() or when a database RuntimeException occurs, updated handleProfileConflict (OVERWRITE) to archive conflicted users via archiveUser instead of deleting them, and updated IP registration limit checks in checkAndValidateByName to count only registered users (filter(User::isRegistered)).
Fixed AuthenticListeners.onPostLogin to kick with kick.database-error if user == null, call markAuthorized(uuid) on auto-login and session resume paths, and invoke onPlayerDisconnect if the player disconnected while onPostLogin was running.
Updated LoginTryListener to track failed attempts by UUID with expireAfterWrite, count wrong passwords across all AuthenticationSource values (LOGIN, TOTP, CHANGE_PASSWORD, SET_EMAIL, PREMIUM_ENABLE, UNREGISTER, TFA_DISABLE) instead of only LOGIN and TOTP, and keep per-IP failure counters independent of AuthenticatedEvent.
Consolidated SetEMailCommand, VerifyEMailCommand, ResetPasswordViaEMailCommand, and ConfirmPasswordReset into /email (set, verify, reset, reset confirm) in EMailCommand, adding checkAuthorized, email syntax validation, per-destination rate limiting (DESTINATION_LIMITER), and constant-time token verification (MessageDigest.isEqual).
Consolidated PremiumEnableCommand and PremiumConfirmCommand into /premium and /premium confirm in PremiumCommand (with checkAuthorized checks), and consolidated TwoFactorAuthCommand and TwoFactorConfirmCommand into /2fa, /2fa confirm, and /2fa disable in TwoFactorCommand (preventing /2fa from overwriting an existing 2FA secret unless disabled first).
Refactored all 8 player command classes (LoginCommand, RegisterCommand, UnregisterCommand, ChangePasswordCommand, PremiumCommand, CrackedCommand, TwoFactorCommand, EMailCommand) to accept concrete Audience sender as parameter 0 (resolved via Command.getPlayer(Audience)) instead of generic P player (which erased to Object.class and failed ACF context lookup in official co.aikar:acf-velocity), added @Private, @Single, checkAuthorized(player), and @CommandPermission annotations, moved ChangePasswordCommand to common.command.commands.authorization, and updated Command.setPassword, /2fa disable, and /librelogin user remove2fa to call revokeSessions(user) (clearing lastAuthentication and ip).
Updated LoginCommand to enforce brute-force lockout timers (error.login-locked), fail closed with totp.unavailable when a 2FA-enabled user logs in while TOTPProvider is unavailable (instead of silently skipping 2FA), and automatically upgrade VerifyOnlyCryptoProvider hashes to the default crypto provider.
Updated RegisterCommand to make <passwordRepeat> optional when require-password-confirmation is false.
Standardized all staff command permissions in LibreLoginCommand under librelogin.admin.*, renamed user 2faoff -> user remove2fa, user emailoff -> user removeemail, and user pass-change -> user setpassword, blocked /librelogin user cracked on passwordless accounts (error.cracked-requires-password-staff), fixed /librelogin user alts to format each alt's own alt.getLastSeen() timestamp (instead of user.getLastSeen()) with a 50-entry cap (MAX_ALTS_SHOWN), and localized status values in /librelogin user info.
Updated CommandProvider to skip command rate-limiting for console issuers, unwrap CompletionException / ExecutionException / InvocationTargetException in the exception handler, bound confirmCache, and unregister commands on plugin disable, and renamed InvalidCommandArgument.getUserFuckUp() to getErrorMessage().
Updated ConfigurateConfiguration to detect ${...} HOCON environment variable substitutions and skip saving the configuration file back to disk when substitutions are present so plaintext secrets do not overwrite placeholders, and updated ConfigurateHelper to propagate CorruptedConfigurationException instead of returning null.
Updated HoconPluginConfiguration to cache resolved configuration values in a ConcurrentHashMap, automatically generate and save a random 32-byte Base64 privacy.ip-hash-salt if left at the default placeholder, and default getNewUUIDCreator() to CRACKED.
Updated HoconMessages to atomically swap the messages map on reload, replace LegacyMessage with GeneralUtil.translateLegacyToMiniMessage, and skip MiniMessage parsing for raw HTML email.* templates.
Fixed JDBC PreparedStatement and ResultSet resource leaks across LibreLoginSQLDatabaseProvider, LibreLoginMySQLDatabaseProvider, LibreLoginPostgreSQLDatabaseProvider, LibreLoginSQLiteDatabaseProvider, AuthMeSQLMigrateReadProvider, and FastLoginSQLMigrateReadProvider by enclosing all statements and result sets in try-with-resources.
Fixed AuthenticMySQLDatabaseConnector, AuthenticPostgreSQLDatabaseConnector, and AuthenticSQLiteDatabaseConnector to record query failures into the circuit breaker and throw RuntimeException instead of calling System.exit(1) on transient SQL errors, and to close HikariDataSource if initial connection validation fails.
Parameterized TABLE_SCHEMA=? in LibreLoginMySQLDatabaseProvider.getColumnNames (replacing SQL string concatenation) and enabled useServerPrepStmts=true in AuthenticMySQLDatabaseConnector.
Updated AuthenticPostgreSQLDatabaseConnector to use the relocated driver xyz.kyngs.librelogin.lib.postgresql.Driver, remove MySQL-specific Hikari properties, and configure sslmode, and updated AuthenticSQLiteDatabaseConnector to use a single-connection pool (maximumPoolSize = 1) and SQLite pragmas (busy_timeout=5000, journal_mode=WAL, synchronous=NORMAL).
Marked all mutable fields in AuthenticUser as volatile for cross-thread visibility, normalized legacy hashes in LibreLoginSQLDatabaseProvider.getUserFromResult, evicted cached user entries when updateUser fails, and updated insertUsers to validate records and run batch inserts inside a transaction with row-by-row fallback (insertOneByOne).
Fixed AuthMeSQLMigrateReadProvider to migrate ip, email, and totp columns, ignore AuthMe's "your@email.com" placeholder, convert second-based epoch timestamps to milliseconds, and abort migration with an IllegalStateException if any account has an unsupported password hash instead of importing it with a null password.
Fixed FastLoginSQLMigrateReadProvider to close the source database connection before performing Mojang API lookups, bound Mojang API throttle retries to MAX_THROTTLE_RETRIES = 5, and isolate per-row update failures.
Fixed AuthenticPremiumProvider to cache negative (cracked) lookups via Optional<PremiumUser>, cache UUID lookups (uuidCache) and transient errors (failureCache, 30s TTL), deduplicate concurrent lookups outside Caffeine locks (inFlight), stop querying fallback resolvers (PlayerDB, Minetools) after an authoritative 204/404 response, and check demo.getAsBoolean() instead of demo != null on Mojang responses.
Fixed AuthenticEMailHandler SMTP TLS/SSL negotiation on port 465 (setSSLOnConnect(true), setSSLCheckServerIdentity(true)), port 587 (setSSLCheckServerIdentity(true)), and default plaintext ports, added 5-second socket connection and read timeouts, restored the thread context classloader in a finally block, and HTML-escaped all user placeholders in email templates.
Fixed Log4JFilter to format parameterized Log4j messages (ParameterizedMessage.format) before checking for sensitive commands, updated LogFilter to parse command roots and /librelogin user subcommands via regex instead of broad substring matching, and added LogFilter.remove() to cleanly unregister filters on disable.
Fixed AuthenticTOTPProvider to zero-pad 6-digit codes with leading zeros (padCode) before verification and generate 128x128 QR BufferedImage instances directly via ZXing MatrixToImageWriter without PNG byte encoding/decoding round-trips.
Fixed ProtocolizeImageProjector and MapDataPacket to attach MapIdComponent on Minecraft 1.20.5+, added clientbound MapDataPacket protocol ID mappings from 1.19.1 through 26.3 (protocol 777), verified packet encodability before projecting, fixed the image.getWidth() != 128 || image.getHeight() != 128 resize condition and BufferedImage.TYPE_CUSTOM fallback, and used readUnsignedByte() for row counts in MapDataPacket.read.
Fixed AuthenticServerHandler to execute backend server pings on a dedicated 2-thread pingExecutor, store limboServers and lobbyServers in thread-safe collections swapped atomically on reload(), and return Optional.empty() when ping == null even if ignore-max-players-from-backend-ping is enabled.
Fixed AuthenticEventProvider to store event subscribers in a CopyOnWriteArraySet and catch/log listener exceptions during dispatch, and fixed AuthenticPlayerBasedEvent to fall back to user.getUuid() when player == null.
Fixed Floodgate authentication bypass in PaperListeners by verifying the Netty channel attribute (floodgateHelper.isFloodgateConnection) on LOGIN_START and checking fromFloodgate(UUID) on pre-login/join instead of checking the username prefix.
Fixed PaperListeners Mojang session verification to URL-encode usernames, check responseCode == HttpURLConnection.HTTP_OK (200) instead of != 204, disconnect HttpURLConnection in a finally block, verify nonces in constant time (MessageDigest.isEqual) with empty-token checks, and immediately invalidate encryptionDataCache entries (keyed by InetSocketAddress) upon receiving WrapperLoginClientEncryptionResponse.
Fixed PaperListeners to track online player names in a thread-safe ConcurrentHashMap.newKeySet() instead of calling Bukkit.getPlayer(username) off the main thread, validate/create users in onPreLogin if not created at the packet stage, bound all internal Caffeine caches (maximumSize(10_000)), dispatch login packets on LOGIN_EXECUTOR with RejectedExecutionException backpressure handling (kick.server-busy), and replace hardcoded English disconnect strings with localized message keys.
Fixed NMS packet encryption in PaperListeners with lazy, multi-version reflection (resolveEncryptionMethods) supporting both (SecretKey) and (Cipher, Cipher) signatures across Paper and Spigot mappings (1.13–26.3), and optimized ProtocolUtil.findNetworkManager to resolve and cache NetworkManager from the Netty pipeline before falling back to a synchronized snapshot of SpigotReflectionUtil.getNetworkManagers().
Fixed paper/Blockers.onTeleport to allow teleports into limbo worlds while cancelling teleports out of limbo, updated onCommand to strip command namespaces (/librelogin:login -> login) and send error.not-authorized, and updated PaperPlatformHandle and PaperLibreLogin to run world creation on the main thread and dispatch teleports via Folia's entity scheduler or Bukkit's main-thread scheduler.
Updated VoidWorldGenerator to disable noise, surface, bedrock, caves, decorations, mobs, and structures, place a barrier block at (0, 69, 0) in legacy generateChunkData, and return a fixed spawn location at (0.5, 70, 0.5).
Fixed velocity/Blockers.onServerConnect to block server switches to non-limbo backends when !authorizationProvider.isAuthorized(player) (previously it only checked isAwaiting2FA), updated onCommand to strip command namespaces and send error.not-authorized, and updated onServerKick to set KickedFromServerEvent.DisconnectPlayer.
Fixed VelocityListeners to execute onPreLogin, onPostLogin, onProfileRequest, chooseServer, and onKick asynchronously via EventTask.async(...), guard against null users in onProfileRequest, and extract the Netty channel using multi-version reflection across Velocity 3.x and 4.x internals.
Fixed VelocityPlatformHandle.movePlayer to use non-blocking CompletableFuture chaining and serialize disconnect reasons with PlainTextComponentSerializer, added a 3-second timeout to VelocityPlatformHandle.ping, and used virt.getHostString() in getPlayersVirtualHost to avoid reverse DNS lookups.
Updated SemanticVersion to implement Comparable<SemanticVersion> and parse null, blank, v-prefixed, -DEV/-SNAPSHOT, or overflowing version strings without throwing exceptions, updated RateLimiter to bound its Caffeine cache (maximumSize(50_000)) with zero-allocation sentinel puts, replaced raw printStackTrace() calls with structured logger calls, removed wildcard imports, and formatted the codebase with Spotless (google-java-format AOSP style).
Removed
Removed runtime dependency loader xyz.kyngs.libby (libby-bukkit, libby-velocity, libby-paper, and xyz.kyngs.libby.plugin) and net.kyori.blossom from the build and bootstrap classes.
Removed bStats telemetry (bstats-velocity, bstats-bukkit, BSTATS_ID constants, and initMetrics hooks) across common, Paper, and Velocity modules.
Removed VelocityRedisBungeeIntegration and the RedisBungee-Bungee dependency in favor of the common Lettuce Redis implementation.
Removed LimboIntegration (NanoLimbo integration on Velocity), the nanolimbovelocity optional plugin dependency, and the limbo-port-range configuration key.
Removed MessageDigestCryptoProvider and LogITMessageDigestCryptoProvider (replaced by LegacyMessageDigestCryptoProvider), at.favre.lib:bcrypt, org.bouncycastle:bcprov-jdk18on, and dev.samstevens.totp:totp.
Removed legacy split-hash conversion methods and Argon2IDHashedPassword from CryptoUtil (replaced by LegacyHashNormalizer).
Removed standalone command classes SetEMailCommand, VerifyEMailCommand, ResetPasswordViaEMailCommand, ConfirmPasswordReset, PremiumEnableCommand, PremiumConfirmCommand, PremiumDisableCommand, TwoFactorAuthCommand, and TwoFactorConfirmCommand, along with legacy command aliases (/passwd, /passch, /log, /autologin, /2faconfirm, /setemail, /verifyemail, /resetpassword, /confirmpasswordreset, user 2faoff, user emailoff, user pass-change).
Removed legacy configuration and message migrators (FirstConfigurationMigrator through EightConfigurationMigrator and FirstMessagesMigrator through ThirdMessagesMigrator) and ConfigurationMigrator.rename.
Removed MultipleSetter, AuthenticPremiumProvider.getUserFromAshcon, GeneralUtil.ASYNC_POOL, paper/ShutdownException (replaced by common/ShutdownException), com.github.kyngs:LegacyMessage, and the GitHub release update checker in AuthenticLibreLogin (replaced by ModrinthUpdateChecker).
Removed .github/workflows (build.yml, release.yml), .github/dependabot.yml, .compose/ Docker test files, .gitattributes, HEADER.txt, licenses/FASTLOGIN_LICENSE, org.cadixdev.licenser, and maven-publish repository configuration in API/build.gradle.kts.