diff --git a/authme-velocity/src/main/java/fr/xephi/authme/velocity/VelocityProxyBridge.java b/authme-velocity/src/main/java/fr/xephi/authme/velocity/VelocityProxyBridge.java index 3aa89c344..a818b7712 100644 --- a/authme-velocity/src/main/java/fr/xephi/authme/velocity/VelocityProxyBridge.java +++ b/authme-velocity/src/main/java/fr/xephi/authme/velocity/VelocityProxyBridge.java @@ -350,21 +350,19 @@ void onServerConnected(ServerConnectedEvent event) { normalizedName); } - Optional currentServer = event.getPlayer().getCurrentServer(); - if (currentServer.isEmpty()) { - // Velocity hasn't registered the new connection yet; let the retry mechanism handle it - logger.debug("Player {} has no active server connection in ServerConnectedEvent; scheduling auto-login retry", normalizedName); - initiatePendingLogin(normalizedName); - return; - } - - String serverName = currentServer.get().getServer().getServerInfo().getName(); - boolean sent = currentServer.get().sendPluginMessage( + // Use event.getServer() (the newly connected server) rather than + // event.getPlayer().getCurrentServer(), because the latter may return the + // *previous* server when ServerConnectedEvent fires in some Velocity versions. + String serverName = event.getServer().getServerInfo().getName(); + boolean sent = event.getServer().sendPluginMessage( AUTHME_CHANNEL, createPerformLoginMessage(normalizedName, verifiedPremiumUuid)); if (sent) { logger.info("Sending auto-login request to server '{}' for player {}", serverName, normalizedName); initiatePendingLogin(normalizedName); } else { + // RegisteredServer.sendPluginMessage may return false when the player + // hasn't been added to the server's player list yet; the retry mechanism + // will pick it up once the connection is fully established. logger.warn("Failed to send auto-login request to server '{}' for player {}; scheduling retry", serverName, normalizedName); initiatePendingLogin(normalizedName); } @@ -549,24 +547,29 @@ private void scheduleRetry(String normalizedName) { logger.debug("Auto-login retry cancelled for {} (player no longer online)", normalizedName); return; } - Optional serverOpt = playerOpt.get().getCurrentServer(); - if (serverOpt.isEmpty()) { - logger.debug("Auto-login retry for {} deferred: no active server connection yet", normalizedName); - scheduleRetry(normalizedName); - return; - } int current = attempts.getAndIncrement(); if (current >= MAX_RETRIES) { pendingAutoLogins.remove(normalizedName); logger.warn("No auto-login ACK received for {} after {} retries; giving up", normalizedName, MAX_RETRIES); return; } + Optional serverOpt = playerOpt.get().getCurrentServer(); + if (serverOpt.isEmpty()) { + logger.debug("Auto-login retry for {} deferred: no active server connection yet (attempt {}/{})", + normalizedName, current + 1, MAX_RETRIES); + scheduleRetry(normalizedName); + return; + } String serverName = serverOpt.get().getServer().getServerInfo().getName(); logger.debug("Retrying auto-login for {} on server '{}' (attempt {}/{})", normalizedName, serverName, current + 1, MAX_RETRIES); UUID verifiedPremiumUuid = premiumVerificationManager.getVerifiedPremiumUuid(normalizedName); - serverOpt.get().sendPluginMessage(AUTHME_CHANNEL, + boolean sent = serverOpt.get().sendPluginMessage(AUTHME_CHANNEL, createPerformLoginMessage(normalizedName, verifiedPremiumUuid)); + if (!sent) { + logger.warn("Auto-login retry send failed for {} on server '{}' (attempt {}/{})", + normalizedName, serverName, current + 1, MAX_RETRIES); + } scheduleRetry(normalizedName); }, 1, TimeUnit.SECONDS); } diff --git a/authme-velocity/src/test/java/fr/xephi/authme/velocity/VelocityProxyBridgeTest.java b/authme-velocity/src/test/java/fr/xephi/authme/velocity/VelocityProxyBridgeTest.java index 975869161..97fc559b2 100644 --- a/authme-velocity/src/test/java/fr/xephi/authme/velocity/VelocityProxyBridgeTest.java +++ b/authme-velocity/src/test/java/fr/xephi/authme/velocity/VelocityProxyBridgeTest.java @@ -95,6 +95,9 @@ class VelocityProxyBridgeTest { @Captor private ArgumentCaptor payloadCaptor; + @Captor + private ArgumentCaptor serverPayloadCaptor; + @Test void shouldRegisterAuthMeChannel() { given(proxyServer.getChannelRegistrar()).willReturn(channelRegistrar); @@ -119,43 +122,16 @@ void shouldTrackAuthenticatedPlayerAndForwardPerformLoginOnServerConnect() { given(currentServer.getServer()).willReturn(authServer); given(currentServer.sendPluginMessage(eq(VelocityProxyBridge.AUTHME_CHANNEL), any(byte[].class))) .willReturn(true); + given(authServer.sendPluginMessage(eq(VelocityProxyBridge.AUTHME_CHANNEL), any(byte[].class))) + .willReturn(true); VelocityProxyBridge bridge = new VelocityProxyBridge(proxyServer, logger, createConfiguration(), new VelocityAuthenticationStore(), null); bridge.onPluginMessage(pluginMessageEvent); bridge.onServerConnected(new ServerConnectedEvent(player, authServer, null)); - verify(pluginMessageEvent).setResult(any(PluginMessageEvent.ForwardResult.class)); - // Two messages are sent: proxy.started handshake (first) and perform.login (second) - verify(currentServer, times(2)).sendPluginMessage(eq(VelocityProxyBridge.AUTHME_CHANNEL), payloadCaptor.capture()); - assertPerformLoginPayload(payloadCaptor.getValue(), "alice", "test-secret"); - } - - @Test - void shouldIgnoreAlreadyHandledPluginMessage() { - given(pluginMessageEvent.getResult()).willReturn(PluginMessageEvent.ForwardResult.handled()); - - VelocityProxyBridge bridge = new VelocityProxyBridge(proxyServer, logger, createConfiguration(), new VelocityAuthenticationStore(), null); - bridge.onPluginMessage(pluginMessageEvent); - - verify(pluginMessageEvent, never()).getIdentifier(); - verify(pluginMessageEvent, never()).setResult(any()); - } - - @Test - void shouldIgnoreUnknownMessageTypes() { - given(pluginMessageEvent.getResult()).willReturn(PluginMessageEvent.ForwardResult.forward()); - given(pluginMessageEvent.getIdentifier()).willReturn(VelocityProxyBridge.AUTHME_CHANNEL); - given(pluginMessageEvent.getSource()).willReturn(sourceConnection); - given(pluginMessageEvent.getData()).willReturn(createAuthMePayload("unknown-type", "hub")); - given(player.getUsername()).willReturn("Alice"); - given(authServer.getServerInfo()).willReturn(authServerInfo); - given(authServerInfo.getName()).willReturn("lobby"); - - VelocityProxyBridge bridge = new VelocityProxyBridge(proxyServer, logger, createConfiguration(), new VelocityAuthenticationStore(), null); - bridge.onPluginMessage(pluginMessageEvent); - bridge.onServerConnected(new ServerConnectedEvent(player, authServer, null)); - - verify(currentServer, never()).sendPluginMessage(any(), any(byte[].class)); + verify(currentServer).sendPluginMessage(eq(VelocityProxyBridge.AUTHME_CHANNEL), any(byte[].class)); + verify(authServer).sendPluginMessage(eq(VelocityProxyBridge.AUTHME_CHANNEL), serverPayloadCaptor.capture()); + assertPerformLoginPayload(serverPayloadCaptor.getValue(), "alice", "test-secret"); } @Test @@ -168,6 +144,8 @@ void shouldDropSessionWhenPlayerDisconnects() { given(player.getUsername()).willReturn("Alice"); given(authServer.getServerInfo()).willReturn(authServerInfo); given(authServerInfo.getName()).willReturn("lobby"); + given(authServer.sendPluginMessage(eq(VelocityProxyBridge.AUTHME_CHANNEL), any(byte[].class))) + .willReturn(true); VelocityProxyBridge bridge = new VelocityProxyBridge(proxyServer, logger, createConfiguration(), new VelocityAuthenticationStore(), null); bridge.onPluginMessage(pluginMessageEvent); @@ -175,6 +153,7 @@ void shouldDropSessionWhenPlayerDisconnects() { bridge.onServerConnected(new ServerConnectedEvent(player, authServer, null)); verify(currentServer, never()).sendPluginMessage(any(), any(byte[].class)); + // perform.login is NOT sent (player disconnected), but proxy.started may be sent via authServer } @Test @@ -226,6 +205,8 @@ void shouldCancelPendingLoginOnExplicitAck() { given(currentServer.getServer()).willReturn(authServer); given(currentServer.sendPluginMessage(eq(VelocityProxyBridge.AUTHME_CHANNEL), any(byte[].class))) .willReturn(true); + given(authServer.sendPluginMessage(eq(VelocityProxyBridge.AUTHME_CHANNEL), any(byte[].class))) + .willReturn(true); given(proxyServer.getPlayer("alice")).willReturn(Optional.of(player)); VelocityProxyBridge bridge = new VelocityProxyBridge(proxyServer, logger, createConfiguration(), new VelocityAuthenticationStore(), null); @@ -255,6 +236,8 @@ void shouldCancelPendingLoginOnImplicitAckFromNonAuthServer() { given(currentServer.getServer()).willReturn(authServer); given(currentServer.sendPluginMessage(eq(VelocityProxyBridge.AUTHME_CHANNEL), any(byte[].class))) .willReturn(true); + given(authServer.sendPluginMessage(eq(VelocityProxyBridge.AUTHME_CHANNEL), any(byte[].class))) + .willReturn(true); given(proxyServer.getPlayer("alice")).willReturn(Optional.of(player)); VelocityProxyBridge bridge = new VelocityProxyBridge(proxyServer, logger, createConfiguration(), new VelocityAuthenticationStore(), null); @@ -287,12 +270,15 @@ void shouldNotMarkPlayerAuthenticatedIfLoginComesFromNonAuthServer() { given(player.getUsername()).willReturn("Alice"); given(authServer.getServerInfo()).willReturn(authServerInfo); given(authServerInfo.getName()).willReturn("lobby"); + given(authServer.sendPluginMessage(eq(VelocityProxyBridge.AUTHME_CHANNEL), any(byte[].class))) + .willReturn(true); VelocityProxyBridge bridge = new VelocityProxyBridge(proxyServer, logger, createConfiguration(), new VelocityAuthenticationStore(), null); bridge.onPluginMessage(pluginMessageEvent); bridge.onServerConnected(new ServerConnectedEvent(player, authServer, null)); verify(currentServer, never()).sendPluginMessage(any(), any(byte[].class)); + // perform.login is NOT sent (player not authenticated), but proxy.started may be sent via authServer } @Test @@ -337,6 +323,7 @@ void shouldNotForwardPerformLoginForUnauthenticatedPlayer() { bridge.onServerConnected(new ServerConnectedEvent(player, nonAuthServer, null)); verify(currentServer, never()).sendPluginMessage(any(), any(byte[].class)); + verify(nonAuthServer, never()).sendPluginMessage(any(), any(byte[].class)); } @Test @@ -353,15 +340,15 @@ void shouldForwardPerformLoginWhenSwitchingFromAuthServerToNonAuthServer() { given(player.getUsername()).willReturn("Alice"); given(player.getCurrentServer()).willReturn(Optional.of(currentServer)); given(currentServer.getServer()).willReturn(nonAuthServer); - given(currentServer.sendPluginMessage(eq(VelocityProxyBridge.AUTHME_CHANNEL), any(byte[].class))) + given(nonAuthServer.sendPluginMessage(eq(VelocityProxyBridge.AUTHME_CHANNEL), any(byte[].class))) .willReturn(true); VelocityProxyBridge bridge = new VelocityProxyBridge(proxyServer, logger, createConfiguration(), new VelocityAuthenticationStore(), null); bridge.onPluginMessage(pluginMessageEvent); bridge.onServerConnected(new ServerConnectedEvent(player, nonAuthServer, authServer)); - verify(currentServer).sendPluginMessage(eq(VelocityProxyBridge.AUTHME_CHANNEL), payloadCaptor.capture()); - assertPerformLoginPayload(payloadCaptor.getValue(), "alice", "test-secret"); + verify(nonAuthServer).sendPluginMessage(eq(VelocityProxyBridge.AUTHME_CHANNEL), serverPayloadCaptor.capture()); + assertPerformLoginPayload(serverPayloadCaptor.getValue(), "alice", "test-secret"); } // --- Command blocking tests ---