Skip to content

Commit e4b68dd

Browse files
committed
fix(premium): revert user uuid to offline one on need
1 parent a506803 commit e4b68dd

4 files changed

Lines changed: 64 additions & 0 deletions

File tree

authme-core/src/main/java/fr/xephi/authme/listener/PlayerListener.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import fr.xephi.authme.permission.PermissionsManager;
1111
import fr.xephi.authme.permission.PlayerStatePermission;
1212
import fr.xephi.authme.platform.ChatAdapter;
13+
import fr.xephi.authme.platform.EventRegistrationAdapter;
1314
import fr.xephi.authme.platform.TeleportAdapter;
1415
import fr.xephi.authme.process.Management;
1516
import fr.xephi.authme.service.AntiBotService;
@@ -19,7 +20,9 @@
1920
import fr.xephi.authme.service.ValidationService;
2021
import fr.xephi.authme.settings.Settings;
2122
import fr.xephi.authme.settings.SpawnLoader;
23+
import fr.xephi.authme.service.PremiumLoginVerifier;
2224
import fr.xephi.authme.settings.properties.HooksSettings;
25+
import fr.xephi.authme.settings.properties.PremiumSettings;
2326
import fr.xephi.authme.settings.properties.RegistrationSettings;
2427
import fr.xephi.authme.settings.properties.RestrictionSettings;
2528
import org.bukkit.ChatColor;
@@ -59,6 +62,7 @@
5962
import org.bukkit.inventory.InventoryView;
6063

6164
import javax.inject.Inject;
65+
import java.nio.charset.StandardCharsets;
6266
import java.util.Locale;
6367
import java.util.Set;
6468
import java.util.UUID;
@@ -106,6 +110,10 @@ public class PlayerListener implements Listener {
106110
private ChatAdapter chatAdapter;
107111
@Inject
108112
private TeleportAdapter teleportAdapter;
113+
@Inject
114+
private PremiumLoginVerifier premiumLoginVerifier;
115+
@Inject
116+
private EventRegistrationAdapter eventRegistrationAdapter;
109117

110118
// Lowest priority to apply fast protection checks
111119
@EventHandler(priority = EventPriority.LOWEST)
@@ -164,6 +172,7 @@ public void onAsyncPlayerPreLoginEventHighest(AsyncPlayerPreLoginEvent event) {
164172
onJoinVerifier.checkNameCasing(name, auth);
165173
final String ip = event.getAddress().getHostAddress();
166174
onJoinVerifier.checkPlayerCountry(name, ip, isAuthAvailable);
175+
normalizePremiumUuidIfNeeded(event, name, auth);
167176
} catch (FailedVerificationException e) {
168177
event.setKickMessage(messages.retrieveSingle(name, e.getReason(), e.getArgs()));
169178
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
@@ -247,6 +256,36 @@ public void onPlayerQuit(PlayerQuitEvent event) {
247256
management.performQuit(player);
248257
}
249258

259+
/**
260+
* When the backend is in offline-mode but the proxy has forwarded a Mojang UUID (v4) for a
261+
* premium player, replaces the event UUID with the deterministic offline UUID so that all
262+
* plugins — permissions, inventories, etc. — consistently use the same identifier.
263+
*
264+
* The Mojang UUID is saved in {@link PremiumLoginVerifier} so that
265+
* {@code AsynchronousJoin#canBypassWithPremium} can still verify premium identity via the
266+
* existing {@code getVerifiedUuid} path without requiring the PacketEvents handshake.
267+
*
268+
* UUID replacement is platform-specific: Paper/Folia implement it via
269+
* {@link EventRegistrationAdapter#normalizePreLoginUuid}; Spigot silently skips it.
270+
*/
271+
private void normalizePremiumUuidIfNeeded(AsyncPlayerPreLoginEvent event, String name, PlayerAuth auth) {
272+
if (event.getUniqueId().version() != 4
273+
|| !Boolean.TRUE.equals(settings.getProperty(HooksSettings.BUNGEECORD))
274+
|| !Boolean.TRUE.equals(settings.getProperty(PremiumSettings.ENABLE_PREMIUM))) {
275+
return;
276+
}
277+
// Skip if the player registered with a v4 UUID (truly online-mode backend or equivalent).
278+
if (auth == null || (auth.getUuid() != null && auth.getUuid().version() == 4)) {
279+
return;
280+
}
281+
UUID mojangUuid = event.getUniqueId();
282+
UUID offlineUuid = UUID.nameUUIDFromBytes(
283+
("OfflinePlayer:" + event.getName()).getBytes(StandardCharsets.UTF_8));
284+
// Store the Mojang UUID so canBypassWithPremium can verify it after the UUID is replaced.
285+
premiumLoginVerifier.storeVerified(name, mojangUuid);
286+
eventRegistrationAdapter.normalizePreLoginUuid(event, offlineUuid);
287+
}
288+
250289
private void saveStateBeforeQuit(Player player) {
251290
Set<EnderPearlRestoreData> pearls = player.getServer().getWorlds().stream()
252291
.flatMap(world -> world.getEntitiesByClass(EnderPearl.class).stream())

authme-core/src/main/java/fr/xephi/authme/platform/EventRegistrationAdapter.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
import fr.xephi.authme.listener.PlayerListener;
66
import fr.xephi.authme.listener.ServerListener;
77
import org.bukkit.event.Listener;
8+
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
89

910
import java.util.ArrayList;
1011
import java.util.List;
12+
import java.util.UUID;
1113

1214
/**
1315
* Supplies the full listener set to register at startup for the active platform.
@@ -19,6 +21,18 @@ public interface EventRegistrationAdapter {
1921
*/
2022
List<Class<? extends Listener>> getListeners();
2123

24+
/**
25+
* Replaces the UUID on the given pre-login event with {@code offlineUuid}.
26+
*
27+
* <p>This is a platform capability: Paper/Folia implement it via
28+
* {@code PlayerProfile.setId()} so the player's session UUID is the offline UUID from
29+
* the start, making it consistent with all other plugins. Spigot provides no stable
30+
* API to change the UUID at this stage and therefore uses the default no-op.
31+
*/
32+
default void normalizePreLoginUuid(AsyncPlayerPreLoginEvent event, UUID offlineUuid) {
33+
// No-op: Spigot does not expose a stable API to change the pre-login UUID
34+
}
35+
2236
/**
2337
* Returns the core listeners shared by all platforms.
2438
*/

authme-core/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,6 +1180,7 @@ private static void verifyNoModifyingCalls(AsyncPlayerPreLoginEvent event) {
11801180
verify(event, atLeast(0)).getLoginResult();
11811181
verify(event, atLeast(0)).getAddress();
11821182
verify(event, atLeast(0)).getName();
1183+
verify(event, atLeast(0)).getUniqueId();
11831184
verifyNoMoreInteractions(event);
11841185
}
11851186

authme-paper-common/src/main/java/fr/xephi/authme/platform/AbstractPaperPlatformAdapter.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
import org.bukkit.Location;
1010
import org.bukkit.entity.Player;
1111
import org.bukkit.event.Listener;
12+
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
1213
import org.bukkit.event.player.PlayerKickEvent;
1314

1415
import java.util.Collection;
1516
import java.util.List;
17+
import java.util.UUID;
1618

1719
/**
1820
* Shared platform adapter behavior for Paper-derived servers.
@@ -74,6 +76,14 @@ public List<Class<? extends Listener>> getListeners() {
7476
return EventRegistrationAdapter.getCommonListeners();
7577
}
7678

79+
@Override
80+
public void normalizePreLoginUuid(AsyncPlayerPreLoginEvent event, UUID offlineUuid) {
81+
com.destroystokyo.paper.profile.PlayerProfile profile = event.getPlayerProfile();
82+
if (profile != null) {
83+
profile.setId(offlineUuid);
84+
}
85+
}
86+
7787
@Override
7888
public boolean isProxyForwardingEnabled() {
7989
if (super.isProxyForwardingEnabled()) {

0 commit comments

Comments
 (0)