f517cc0b8a
Because of some reasons, this patch will be anonymous and hide the original commit address.
204 lines
14 KiB
Diff
204 lines
14 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: MrHua269 <mrhua269@gmail.com>
|
|
Date: Sat, 27 Jun 2026 17:23:22 +0800
|
|
Subject: [PATCH] Leaf Configurable vanilla username check
|
|
|
|
A part of Leaf(https://github.com/Winds-Studio/Leaf/blob/edb0504069139beaa6f39efa4702370c2576b3fc/leaf-server/minecraft-patches/features/0086-Configurable-vanilla-username-check.patch)
|
|
|
|
License: https://github.com/Winds-Studio/Leaf/blob/edb0504069139beaa6f39efa4702370c2576b3fc/LICENSE.md
|
|
|
|
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
|
index 42ee15c813761702031d5a63f0d080b135be2e0f..1736816e26c255645d62696814cd539ee1ffed8d 100644
|
|
--- a/net/minecraft/server/MinecraftServer.java
|
|
+++ b/net/minecraft/server/MinecraftServer.java
|
|
@@ -217,7 +217,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
public static final NameAndId ANONYMOUS_PLAYER_PROFILE = new NameAndId(Util.NIL_UUID, "Anonymous Player");
|
|
public static final String SERVER_THREAD_NAME = "Server thread";
|
|
public LevelStorageSource.LevelStorageAccess storageSource;
|
|
- protected final PlayerDataStorage playerDataStorage;
|
|
+ public final PlayerDataStorage playerDataStorage;
|
|
private final SavedDataStorage savedDataStorage;
|
|
private final List<Runnable> tickables = Lists.newArrayList();
|
|
// Paper - per-level GameRules
|
|
diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
|
index deee051c103f9797f4612e06c12f293ec54724ac..911fb0912c7bc6ab0e29c03a21c6bddb248bbc0a 100644
|
|
--- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
|
+++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
|
@@ -167,11 +167,20 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
|
|
public void handleHello(final ServerboundHelloPacket packet) {
|
|
Validate.validState(this.state == ServerLoginPacketListenerImpl.State.HELLO, "Unexpected hello packet");
|
|
// Paper start - Validate usernames
|
|
- if (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode()
|
|
+ // Leaf start - Configurable vanilla username check
|
|
+ boolean allPrevChecksPassed;
|
|
+ if (me.earthme.luminol.config.modules.misc.UsernameCheckConfig.enabled
|
|
+ && io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode()
|
|
&& io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.performUsernameValidation
|
|
&& !this.iKnowThisMayNotBeTheBestIdeaButPleaseDisableUsernameValidation) {
|
|
- Validate.validState(StringUtil.isReasonablePlayerName(packet.name()), "Invalid characters in username");
|
|
+ allPrevChecksPassed = true;
|
|
+ if (!me.earthme.luminol.config.modules.misc.UsernameCheckConfig.allowOldPlayersJoin) {
|
|
+ Validate.validState(StringUtil.isReasonablePlayerName(packet.name()), "Invalid characters in username");
|
|
+ }
|
|
+ } else {
|
|
+ allPrevChecksPassed = false;
|
|
}
|
|
+ // Leaf end - Configurable vanilla username check
|
|
this.requestedUuid = packet.profileId();
|
|
// Paper end - Validate usernames
|
|
this.requestedUsername = packet.name();
|
|
@@ -197,6 +206,15 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
|
|
authenticatorPool.execute(() -> {
|
|
try {
|
|
GameProfile gameprofile = ServerLoginPacketListenerImpl.this.createOfflineProfile(ServerLoginPacketListenerImpl.this.requestedUsername); // Spigot
|
|
+ // Leaf start - Configurable vanilla username check
|
|
+ if (me.earthme.luminol.config.modules.misc.UsernameCheckConfig.allowOldPlayersJoin) {
|
|
+ if (server.playerDataStorage.load(new net.minecraft.server.players.NameAndId(gameprofile)).orElse(null) != null) {
|
|
+ server.getPlayerList().playedPlayers.add(packet.name());
|
|
+ } else if (allPrevChecksPassed) {
|
|
+ Validate.validState(StringUtil.isReasonablePlayerName(packet.name()), "Invalid characters in username");
|
|
+ }
|
|
+ }
|
|
+ // Leaf end - Configurable vanilla username check
|
|
|
|
gameprofile = ServerLoginPacketListenerImpl.this.callPlayerPreLoginEvents(gameprofile); // Paper - Add more fields to AsyncPlayerPreLoginEvent
|
|
ServerLoginPacketListenerImpl.LOGGER.info("UUID of player {} is {}", gameprofile.name(), gameprofile.id());
|
|
@@ -341,7 +359,7 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
|
|
server.getPluginManager().callEvent(asyncEvent);
|
|
profile = asyncEvent.getPlayerProfile();
|
|
profile.complete(true); // Paper - setPlayerProfileAPI
|
|
- gameprofile = com.destroystokyo.paper.profile.CraftPlayerProfile.asAuthlibCopy(profile);
|
|
+ gameprofile = com.destroystokyo.paper.profile.CraftPlayerProfile.asAuthlibCopyCustomValidation(profile); // Leaf - Configurable vanilla username check
|
|
playerName = gameprofile.name();
|
|
uniqueId = gameprofile.id();
|
|
// Paper end - Add more fields to AsyncPlayerPreLoginEvent
|
|
diff --git a/net/minecraft/server/players/CachedUserNameToIdResolver.java b/net/minecraft/server/players/CachedUserNameToIdResolver.java
|
|
index 7443744e3f256983e52a1cefcaf66082e4a46a7b..b7553611723f7120c66e1f3e51d89df28bd2756b 100644
|
|
--- a/net/minecraft/server/players/CachedUserNameToIdResolver.java
|
|
+++ b/net/minecraft/server/players/CachedUserNameToIdResolver.java
|
|
@@ -67,7 +67,7 @@ public class CachedUserNameToIdResolver implements UserNameToIdResolver {
|
|
}
|
|
|
|
private Optional<NameAndId> lookupGameProfile(final GameProfileRepository profileRepository, final String name) {
|
|
- if (!StringUtil.isValidPlayerName(name)) {
|
|
+ if (!StringUtil.isValidPlayerName(name, false)) { // Leaf - Configurable vanilla username check - Directly return, skip unnecessary following logic
|
|
return this.createUnknownProfile(name);
|
|
}
|
|
|
|
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
|
|
index a8f051e6aff31a723a3ba59842fac3a43f4911fe..2939aa1ecd68504424198dd7c90c71c6c1aa4c80 100644
|
|
--- a/net/minecraft/server/players/PlayerList.java
|
|
+++ b/net/minecraft/server/players/PlayerList.java
|
|
@@ -134,6 +134,7 @@ public abstract class PlayerList {
|
|
private org.bukkit.craftbukkit.CraftServer cserver;
|
|
private final Map<String,ServerPlayer> playersByName = new java.util.HashMap<>();
|
|
public @Nullable String collideRuleTeamName; // Paper - Configurable player collision
|
|
+ public final List<String> playedPlayers = new java.util.concurrent.CopyOnWriteArrayList<>(); // Leaf - Configurable vanilla username check
|
|
|
|
// Folia start - region threading
|
|
private final Object connectionsStateLock = new Object();
|
|
@@ -581,6 +582,7 @@ public abstract class PlayerList {
|
|
player.getAdvancements().clearTriggers();
|
|
this.players.remove(player);
|
|
this.playersByName.remove(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot
|
|
+ if (me.earthme.luminol.config.modules.misc.UsernameCheckConfig.allowOldPlayersJoin) this.playedPlayers.remove(player.getGameProfile().name()); // Leaf - Configurable vanilla username check
|
|
this.server.getCustomBossEvents().onPlayerDisconnect(player);
|
|
UUID uuid = player.getUUID();
|
|
ServerPlayer serverPlayer = this.playersByUUID.get(uuid);
|
|
diff --git a/net/minecraft/util/StringUtil.java b/net/minecraft/util/StringUtil.java
|
|
index 7957e0cfc43909c5268698a11c4933d20b4d9155..3de226b84f5c72ae277384b3fda4580a7fa2b43d 100644
|
|
--- a/net/minecraft/util/StringUtil.java
|
|
+++ b/net/minecraft/util/StringUtil.java
|
|
@@ -64,6 +64,15 @@ public class StringUtil {
|
|
}
|
|
|
|
public static boolean isValidPlayerName(final String name) {
|
|
+ // Leaf start - Configurable vanilla username check
|
|
+ return isValidPlayerName(name, me.earthme.luminol.config.modules.misc.UsernameCheckConfig.shouldSkipNonPlayerNameCheck());
|
|
+ }
|
|
+ public static boolean isValidPlayerNameVanilla(final String name) {
|
|
+ return name.length() <= 16 && name.chars().filter(i -> i <= 32 || i >= 127).findAny().isEmpty();
|
|
+ }
|
|
+ public static boolean isValidPlayerName(final String name, final boolean bypassCheck) {
|
|
+ if (bypassCheck) return name.length() <= 16;
|
|
+ // Leaf end - Configurable vanilla username check
|
|
return name.length() <= 16 && name.chars().filter(c -> c <= 32 || c >= 127).findAny().isEmpty();
|
|
}
|
|
|
|
@@ -87,6 +96,12 @@ public class StringUtil {
|
|
|
|
// Paper start - Username validation
|
|
public static boolean isReasonablePlayerName(final String name) {
|
|
+ // Leaf start - Configurable vanilla username check
|
|
+ if (me.earthme.luminol.config.modules.misc.UsernameCheckConfig.allowOldPlayersJoin && net.minecraft.server.MinecraftServer.getServer().getPlayerList().playedPlayers.contains(name)) return true;
|
|
+ if (me.earthme.luminol.config.modules.misc.UsernameCheckConfig.useCustomUsernameRegex()) {
|
|
+ return me.earthme.luminol.config.modules.misc.UsernameCheckConfig.usernameRegex.matcher(name).matches() && name.length() <= 16; // Leaf - Configurable username check
|
|
+ }
|
|
+ // Leaf end - Configurable vanilla username check
|
|
if (name.isEmpty() || name.length() > 16) {
|
|
return false;
|
|
}
|
|
diff --git a/net/minecraft/world/item/component/ResolvableProfile.java b/net/minecraft/world/item/component/ResolvableProfile.java
|
|
index f1b62a6e0d8176d8ba1875e78cc730ee55b1b0c1..3312a751bdd71080bf429990490061bfbf86b758 100644
|
|
--- a/net/minecraft/world/item/component/ResolvableProfile.java
|
|
+++ b/net/minecraft/world/item/component/ResolvableProfile.java
|
|
@@ -68,6 +68,30 @@ public abstract sealed class ResolvableProfile implements TooltipProvider permit
|
|
|
|
public abstract Either<GameProfile, ResolvableProfile.Partial> unpack();
|
|
|
|
+ // Leaf start - Configurable vanilla username check - Enforce skull validation
|
|
+ private static Either<String, UUID> sanitizeDynamicPlayerName(final Either<String, UUID> nameOrId) {
|
|
+ if (nameOrId.left().isEmpty()) {
|
|
+ return nameOrId;
|
|
+ }
|
|
+ return me.earthme.luminol.config.modules.misc.UsernameCheckConfig.enforceSkullValidation && !net.minecraft.util.StringUtil.isValidPlayerNameVanilla(nameOrId.left().get()) ? Either.left("INVALID_OWNER") : nameOrId;
|
|
+ }
|
|
+
|
|
+ private static Optional<String> sanitizePartialPlayerName(final Optional<String> name) {
|
|
+ if (name.isEmpty()) {
|
|
+ return name;
|
|
+ }
|
|
+ return me.earthme.luminol.config.modules.misc.UsernameCheckConfig.enforceSkullValidation && !net.minecraft.util.StringUtil.isValidPlayerNameVanilla(name.get()) ? Optional.of("INVALID_OWNER") : name;
|
|
+ }
|
|
+
|
|
+ private static Either<GameProfile, ResolvableProfile.Partial> sanitizeStaticPlayerName(final Either<GameProfile, ResolvableProfile.Partial> contents) {
|
|
+ if (contents.left().isEmpty()) {
|
|
+ return contents;
|
|
+ }
|
|
+ GameProfile gameProfile = contents.left().get();
|
|
+ return me.earthme.luminol.config.modules.misc.UsernameCheckConfig.enforceSkullValidation && !net.minecraft.util.StringUtil.isValidPlayerNameVanilla(gameProfile.name()) ? Either.left(new GameProfile(gameProfile.id(), "INVALID_OWNER", gameProfile.properties())) : contents;
|
|
+ }
|
|
+ // Leaf end - Configurable vanilla username check - Enforce skull validation
|
|
+
|
|
protected ResolvableProfile(final GameProfile partialProfile, final PlayerSkin.Patch skinPatch) {
|
|
this.partialProfile = partialProfile;
|
|
this.skinPatch = skinPatch;
|
|
@@ -96,6 +120,7 @@ public abstract sealed class ResolvableProfile implements TooltipProvider permit
|
|
private final Either<String, UUID> nameOrId;
|
|
|
|
public Dynamic(final Either<String, UUID> nameOrId, final PlayerSkin.Patch skinPatch) {
|
|
+ sanitizeDynamicPlayerName(nameOrId); // Leaf - Configurable vanilla username check
|
|
super(ResolvableProfile.createPartialProfile(nameOrId.left(), nameOrId.right(), PropertyMap.EMPTY), skinPatch);
|
|
this.nameOrId = nameOrId;
|
|
}
|
|
@@ -135,6 +160,11 @@ public abstract sealed class ResolvableProfile implements TooltipProvider permit
|
|
}
|
|
|
|
public record Partial(Optional<String> name, Optional<UUID> id, PropertyMap properties) {
|
|
+ // Leaf start - Configurable vanilla username check
|
|
+ public Partial {
|
|
+ name = ResolvableProfile.sanitizePartialPlayerName(name);
|
|
+ }
|
|
+ // Leaf end - Configurable vanilla username check
|
|
public static final ResolvableProfile.Partial EMPTY = new ResolvableProfile.Partial(Optional.empty(), Optional.empty(), PropertyMap.EMPTY);
|
|
public static final MapCodec<ResolvableProfile.Partial> MAP_CODEC = RecordCodecBuilder.mapCodec(
|
|
i -> i.group(
|
|
@@ -165,6 +195,7 @@ public abstract sealed class ResolvableProfile implements TooltipProvider permit
|
|
private final Either<GameProfile, ResolvableProfile.Partial> contents;
|
|
|
|
public Static(final Either<GameProfile, ResolvableProfile.Partial> contents, final PlayerSkin.Patch skinPatch) {
|
|
+ sanitizeStaticPlayerName(contents); // Leaf - Configurable vanilla username check
|
|
super(contents.map(gameProfile -> (GameProfile)gameProfile, ResolvableProfile.Partial::createProfile), skinPatch);
|
|
this.contents = contents;
|
|
}
|