9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2026-01-06 15:51:31 +00:00

Fix configurable username check bugs (#572)

* Fix configurable username check bugs

* Format

* cleanup config load logic
This commit is contained in:
Creeam
2025-12-31 03:41:38 +14:00
committed by GitHub
parent c7064288f1
commit 8fda84ea96
5 changed files with 162 additions and 36 deletions

View File

@@ -5,7 +5,7 @@ Subject: [PATCH] Configurable vanilla username check
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index fb585637383db4592f97f0c9040ffa86afb43c6a..29417442b9379779a078e8fc819035120ddf0108 100644
index fb585637383db4592f97f0c9040ffa86afb43c6a..5381986c9d283d3b22c59d80acf246dab99771c5 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -2466,7 +2466,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -13,50 +13,64 @@ index fb585637383db4592f97f0c9040ffa86afb43c6a..29417442b9379779a078e8fc81903512
String scoreboardName = getScoreboardName();
String playerListName = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().serialize(getBukkitEntity().playerListName());
- String[] split = playerListName.split(scoreboardName);
+ String[] split = playerListName.split(java.util.regex.Pattern.quote(scoreboardName)); // Leaf - Vanilla username check - Sanitize name input
+ String[] split = playerListName.split(java.util.regex.Pattern.quote(scoreboardName)); // Leaf - Configurable vanilla username check - Sanitize name input
String prefix = (split.length > 0 ? split[0] : "").replace(org.purpurmc.purpur.PurpurConfig.afkTabListPrefix, "");
String suffix = (split.length > 1 ? split[1] : "").replace(org.purpurmc.purpur.PurpurConfig.afkTabListSuffix, "");
if (afk) {
diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
index 6d320ed179393e47398c44f2ba2b2285016f349e..0e7a4d6a3b33a8bd9eebd854b87af269cbec6ea6 100644
index 6d320ed179393e47398c44f2ba2b2285016f349e..19a56f8c980f739bff54fb067ccf293286f16faf 100644
--- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
@@ -163,10 +163,15 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
@@ -163,11 +163,20 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
public void handleHello(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()
+ if (!org.dreeam.leaf.config.modules.misc.VanillaUsernameCheck.removeAllCheck // Leaf - Remove vanilla username check
+ // Leaf start - Configurable vanilla username check
+ boolean allPrevChecksPassed;
+ if (!org.dreeam.leaf.config.modules.misc.VanillaUsernameCheck.removeAllCheck
+ && 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");
+ // Leaf start - Vanilla username check
+ allPrevChecksPassed = true;
+ if (!org.dreeam.leaf.config.modules.misc.VanillaUsernameCheck.allowOldPlayersJoin) {
+ Validate.validState(StringUtil.isReasonablePlayerName(packet.name()), "Invalid characters in username");
+ }
+ // Leaf end - Vanilla username check
+ } else {
+ allPrevChecksPassed = false;
}
+ // Leaf end - Configurable vanilla username check
this.requestedUuid = packet.profileId();
// Paper end - Validate usernames
@@ -194,6 +199,15 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
this.requestedUsername = packet.name();
@@ -194,6 +203,15 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
authenticatorPool.execute(() -> {
try {
GameProfile gameprofile = ServerLoginPacketListenerImpl.this.createOfflineProfile(ServerLoginPacketListenerImpl.this.requestedUsername); // Spigot
+ // Leaf start - Vanilla username check
+ // Leaf start - Configurable vanilla username check
+ if (org.dreeam.leaf.config.modules.misc.VanillaUsernameCheck.allowOldPlayersJoin) {
+ if (server.playerDataStorage.load(gameprofile.getName(), gameprofile.getId().toString(), gameprofile.getId(), net.minecraft.util.ProblemReporter.DISCARDING).orElse(null) != null) {
+ server.getPlayerList().playedPlayers.add(packet.name());
+ } else {
+ } else if (allPrevChecksPassed) {
+ Validate.validState(StringUtil.isReasonablePlayerName(packet.name()), "Invalid characters in username");
+ }
+ }
+ // Leaf end - Vanilla username check
+ // 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.getName(), gameprofile.getId());
@@ -335,7 +353,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.getName();
uniqueId = gameprofile.getId();
// Paper end - Add more fields to AsyncPlayerPreLoginEvent
diff --git a/net/minecraft/server/players/GameProfileCache.java b/net/minecraft/server/players/GameProfileCache.java
index 066f84df5c31242ab542932f1e243369d0e766e2..4faa87d18c2e55d6320f9e8ec91862b9e58d26b5 100644
index 066f84df5c31242ab542932f1e243369d0e766e2..223f39d2e6d82815af14d437a8c886ae591be597 100644
--- a/net/minecraft/server/players/GameProfileCache.java
+++ b/net/minecraft/server/players/GameProfileCache.java
@@ -75,7 +75,7 @@ public class GameProfileCache {
@@ -64,19 +78,19 @@ index 066f84df5c31242ab542932f1e243369d0e766e2..4faa87d18c2e55d6320f9e8ec91862b9
private static Optional<GameProfile> lookupGameProfile(GameProfileRepository profileRepo, String name) {
- if (!StringUtil.isValidPlayerName(name)) {
+ if (!StringUtil.isValidPlayerName(name, false)) { // Leaf start - Remove vanilla username check - Directly return, skip unnecessary following logic
+ if (!StringUtil.isValidPlayerName(name, false)) { // Leaf - Configurable vanilla username check - Directly return, skip unnecessary following logic
return createUnknownProfile(name);
} else {
final boolean shouldLookup = !org.apache.commons.lang3.StringUtils.isBlank(name) // Paper - Don't lookup a profile with a blank name
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 6396083993d248683b887774d2db3f3f03825033..fbf4e548f4377c7155a3a02c3b679a0322ed6bc0 100644
index 6396083993d248683b887774d2db3f3f03825033..883869d39319e0f98c24d356beab2705d2fa769c 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -136,6 +136,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 - Vanilla username check
+ public final List<String> playedPlayers = new java.util.concurrent.CopyOnWriteArrayList<>(); // Leaf - Configurable vanilla username check
public PlayerList(MinecraftServer server, LayeredRegistryAccess<RegistryLayer> registries, PlayerDataStorage playerIo, int maxPlayers) {
this.cserver = server.server = new org.bukkit.craftbukkit.CraftServer((net.minecraft.server.dedicated.DedicatedServer) server, this);
@@ -84,61 +98,60 @@ index 6396083993d248683b887774d2db3f3f03825033..fbf4e548f4377c7155a3a02c3b679a03
player.getAdvancements().stopListening();
this.players.remove(player);
this.playersByName.remove(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot
+ if (org.dreeam.leaf.config.modules.misc.VanillaUsernameCheck.allowOldPlayersJoin) this.playedPlayers.remove(player.getGameProfile().getName()); // Leaf - Vanilla username check
+ if (org.dreeam.leaf.config.modules.misc.VanillaUsernameCheck.allowOldPlayersJoin) this.playedPlayers.remove(player.getGameProfile().getName()); // Leaf - Configurable vanilla username check
this.removeFromSendAllPlayerInfoBuckets(player); // Gale - Purpur - spread out sending all player info
this.server.getCustomBossEvents().onPlayerDisconnect(player);
UUID uuid = player.getUUID();
diff --git a/net/minecraft/util/StringUtil.java b/net/minecraft/util/StringUtil.java
index c3a99fe7b49858bc0ca9a7f800b0db40465f6901..2f8a5d0b94b548dfc62f2c40bf10218d34c17574 100644
index c3a99fe7b49858bc0ca9a7f800b0db40465f6901..d344857a20bfed20d8ffd422c0e6f2bd96b2ae8f 100644
--- a/net/minecraft/util/StringUtil.java
+++ b/net/minecraft/util/StringUtil.java
@@ -64,6 +64,16 @@ public class StringUtil {
@@ -64,6 +64,15 @@ public class StringUtil {
}
public static boolean isValidPlayerName(String playerName) {
+ // Leaf start - Remove vanilla username check
+ return isValidPlayerName(playerName, org.dreeam.leaf.config.modules.misc.VanillaUsernameCheck.removeAllCheck);
+ // Leaf start - Configurable vanilla username check
+ return isValidPlayerName(playerName, org.dreeam.leaf.config.modules.misc.VanillaUsernameCheck.shouldSkipNonPlayerNameCheck());
+ }
+ public static boolean isValidPlayerNameVanilla(String playerName) {
+ return playerName.length() <= 16 && playerName.chars().filter(i -> i <= 32 || i >= 127).findAny().isEmpty();
+ }
+ public static boolean isValidPlayerName(String playerName, boolean bypassCheck) {
+ if (bypassCheck) return playerName.length() <= 16;
+ if (true) return isReasonablePlayerName(playerName); // Leaf - Fix Purpur username validation config
+ // Leaf end - Remove vanilla username check
+ // Leaf end - Configurable vanilla username check
return playerName.length() <= 16 && playerName.chars().filter(i -> i <= 32 || i >= 127).findAny().isEmpty();
}
@@ -87,7 +97,12 @@ public class StringUtil {
@@ -87,7 +96,12 @@ public class StringUtil {
// Paper start - Username validation
public static boolean isReasonablePlayerName(final String name) {
- if (true) return org.purpurmc.purpur.PurpurConfig.usernameValidCharactersPattern.matcher(name).matches(); // Purpur - Configurable valid characters for usernames
+ // Leaf start - Vanilla username check
+ if (true) {
+ if (org.dreeam.leaf.config.modules.misc.VanillaUsernameCheck.allowOldPlayersJoin && net.minecraft.server.MinecraftServer.getServer().getPlayerList().playedPlayers.contains(name)) return true;
+ return org.purpurmc.purpur.PurpurConfig.usernameValidCharactersPattern.matcher(name).matches() && name.length() <= 16; // Purpur - Configurable valid characters for usernames // Leaf - Fix Purpur username validation config - Add length check
+ // Leaf start - Configurable vanilla username check
+ if (org.dreeam.leaf.config.modules.misc.VanillaUsernameCheck.allowOldPlayersJoin && net.minecraft.server.MinecraftServer.getServer().getPlayerList().playedPlayers.contains(name)) return true;
+ if (org.dreeam.leaf.config.modules.misc.VanillaUsernameCheck.useUsernameRegex) {
+ return org.dreeam.leaf.config.modules.misc.VanillaUsernameCheck.usernameRegex.matcher(name).matches() && name.length() <= 16; // Purpur - Configurable valid characters for usernames // Leaf - use our own config
+ }
+ // Leaf end - Vanilla 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 48517612ff40c83069a52b817a1af9e6ef180db5..5e6441196a697be73f79e4776209791627a5ecc2 100644
index 48517612ff40c83069a52b817a1af9e6ef180db5..c1937cc39c75c03896f37ea1b516cc5b72e74969 100644
--- a/net/minecraft/world/item/component/ResolvableProfile.java
+++ b/net/minecraft/world/item/component/ResolvableProfile.java
@@ -39,6 +39,15 @@ public record ResolvableProfile(Optional<String> name, Optional<UUID> id, Proper
ResolvableProfile::new
);
+ // Leaf start - Vanilla username check - Enforce skull validation
+ // Leaf start - Configurable vanilla username check - Enforce skull validation
+ public ResolvableProfile {
+ name = sanitizePlayerName(name);
+ }
+ private static Optional<String> sanitizePlayerName(Optional<String> name) {
+ return org.dreeam.leaf.config.modules.misc.VanillaUsernameCheck.enforceSkullValidation && !net.minecraft.util.StringUtil.isValidPlayerNameVanilla(name.orElse("NULL_OWNER")) ? Optional.of("INVALID_OWNER") : name;
+ }
+ // Leaf end - Vanilla username check - Enforce skull validation
+ // Leaf end - Configurable vanilla username check - Enforce skull validation
+
public ResolvableProfile(Optional<String> name, Optional<UUID> id, PropertyMap properties) {
this(name, id, properties, createGameProfile(id, name, properties));

View File

@@ -342,7 +342,7 @@ index 1d128d0613d92eb95a753be33a1134c857d0e388..c50cb9bfcd06ec54c0a43d013aa074fb
ServerLevel.this.updateSleepingPlayerList();
}
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 76785d9b11593cc9304e3d16962ddddba2ff8834..e0ae31621ececc54b9e38e524c2b56ffb02949a1 100644
index 4fbe3d2a7e157f1f06bd0929ee10efd87426554d..fdd2ad453192cfac8dd5310266cbab3bdb5743c6 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -131,6 +131,7 @@ public abstract class PlayerList {
@@ -532,7 +532,7 @@ index 76785d9b11593cc9304e3d16962ddddba2ff8834..e0ae31621ececc54b9e38e524c2b56ff
this.players.remove(player);
+ this.realPlayers.remove(player); // Leaves - replay api
this.playersByName.remove(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot
if (org.dreeam.leaf.config.modules.misc.VanillaUsernameCheck.allowOldPlayersJoin) this.playedPlayers.remove(player.getGameProfile().getName()); // Leaf - Vanilla username check
if (org.dreeam.leaf.config.modules.misc.VanillaUsernameCheck.allowOldPlayersJoin) this.playedPlayers.remove(player.getGameProfile().getName()); // Leaf - Configurable vanilla username check
this.removeFromSendAllPlayerInfoBuckets(player); // Gale - Purpur - spread out sending all player info
@@ -1011,10 +1158,10 @@ public abstract class PlayerList {
}

View File

@@ -110,10 +110,10 @@ index 7f3379a617932f1e0b0344df75566d2dc6612359..99d9d5048440e535ed6be33df39bf7ae
try {
PlayerList playerList = this.server.getPlayerList();
diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
index 970c6f20a3e995dc1b82c3fbabd6ab01a51f22c0..35e8ac5e7a2e4719d407bdc61579d0bc14cc4712 100644
index a485b708858b6bb87a7ae6ca7f0b2a13dc3b07ed..565870996264ba5bd93b6eaf93b9c9fd8a3ce433 100644
--- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
@@ -429,11 +429,31 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
@@ -433,11 +433,31 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
this.disconnect(ServerCommonPacketListenerImpl.DISCONNECT_UNEXPECTED_QUERY);
}

View File

@@ -0,0 +1,78 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com>
Date: Wed, 12 Oct 2022 14:36:58 -0400
Subject: [PATCH] Configurable vanilla username check
diff --git a/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java b/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java
index db43329d57144471d0c9ce0eed92d2f1bd05f120..d65bde85ecae29104c5c34380f164252df9e4515 100644
--- a/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java
+++ b/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java
@@ -62,6 +62,14 @@ public class CraftPlayerProfile implements PlayerProfile, SharedPlayerProfile {
copyProfileProperties(resolvableProfile.gameProfile(), this.profile);
}
+ // Leaf start - Configurable vanilla username check
+ public CraftPlayerProfile(UUID id, String name, boolean useCustomValidation) {
+ this.profile = createAuthLibProfile(id, name, useCustomValidation);
+ this.emptyName = name == null;
+ this.emptyUUID = id == null;
+ }
+ // Leaf end - Configurable vanilla username check
+
@Override
public boolean hasProperty(String property) {
return profile.getProperties().containsKey(property);
@@ -195,6 +203,14 @@ public class CraftPlayerProfile implements PlayerProfile, SharedPlayerProfile {
return clone;
}
+ // Leaf start - Configurable vanilla username check
+ public CraftPlayerProfile cloneCustom() {
+ CraftPlayerProfile clone = new CraftPlayerProfile(this.getId(), this.getName(), true);
+ clone.setProperties(getProperties());
+ return clone;
+ }
+ // Leaf end - Configurable vanilla username check
+
@Override
public boolean isComplete() {
return this.getId() != null && StringUtils.isNotBlank(this.getName());
@@ -369,15 +385,21 @@ public class CraftPlayerProfile implements PlayerProfile, SharedPlayerProfile {
}
}
- private static GameProfile createAuthLibProfile(UUID uniqueId, String name) {
+ // Leaf start - Configurable vanilla username check
+ private static GameProfile createAuthLibProfile(UUID uniqueId, String name, boolean useCustomValidation) {
Preconditions.checkArgument(name == null || name.length() <= 16, "Name cannot be longer than 16 characters");
- Preconditions.checkArgument(name == null || StringUtil.isValidPlayerName(name), "The name of the profile contains invalid characters: %s", name);
+ Preconditions.checkArgument(name == null || (useCustomValidation ? StringUtil.isReasonablePlayerName(name) : StringUtil.isValidPlayerName(name)), "The name of the profile contains invalid characters: %s", name);
return new GameProfile(
uniqueId != null ? uniqueId : Util.NIL_UUID,
name != null ? name : ""
);
}
+ private static GameProfile createAuthLibProfile(UUID uniqueId, String name) {
+ return createAuthLibProfile(uniqueId, name, false);
+ }
+ // Leaf end - Configurable vanilla username check
+
private static ProfileProperty toBukkit(Property property) {
return new ProfileProperty(property.name(), property.value(), property.signature());
}
@@ -401,6 +423,13 @@ public class CraftPlayerProfile implements PlayerProfile, SharedPlayerProfile {
return asAuthlib(craft.clone());
}
+ // Leaf start - Configurable vanilla username check
+ public static GameProfile asAuthlibCopyCustomValidation(PlayerProfile profile) {
+ CraftPlayerProfile craft = ((CraftPlayerProfile) profile);
+ return asAuthlib(craft.cloneCustom());
+ }
+ // Leaf end - Configurable vanilla username check
+
public static GameProfile asAuthlib(PlayerProfile profile) {
CraftPlayerProfile craft = ((CraftPlayerProfile) profile);
return craft.getGameProfile();

View File

@@ -2,19 +2,28 @@ package org.dreeam.leaf.config.modules.misc;
import org.dreeam.leaf.config.ConfigModules;
import org.dreeam.leaf.config.EnumConfigCategory;
import org.dreeam.leaf.config.LeafConfig;
import org.dreeam.leaf.config.annotations.Experimental;
import java.util.regex.Pattern;
public class VanillaUsernameCheck extends ConfigModules {
public String getBasePath() {
return EnumConfigCategory.MISC.getBaseKeyName() + ".vanilla-username-check";
}
@Experimental
@Deprecated
public static boolean removeAllCheck = false;
public static boolean enforceSkullValidation = true;
@Experimental
public static boolean allowOldPlayersJoin = false;
public static boolean useUsernameRegex = false;
private static final String defaultRegexString = "^[a-zA-Z0-9_.]*$";
public static Pattern usernameRegex = Pattern.compile(defaultRegexString);
public static boolean shouldSkipNonPlayerNameCheck() { // helper
return removeAllCheck || useUsernameRegex;
}
@Override
public void onLoaded() {
@@ -38,5 +47,31 @@ public class VanillaUsernameCheck extends ConfigModules {
"""
允许老玩家加入修改用户名验证正则后的服务器,
即使他们的用户名不满足修改后的正则."""));
useUsernameRegex = config.getBoolean(getBasePath() + ".use-username-regex", useUsernameRegex, config.pickStringRegionBased("""
Use username regex to validate usernames,
allowing only characters specified in the regex.""",
"""
使用用户名正则来验证用户名,
只允许正则指定的字符."""));
String regexString = config.getString(getBasePath() + ".username-regex", defaultRegexString, config.pickStringRegionBased(
"""
Username regex,
specifying the characters allowed in usernames.
Default: %s""".formatted(defaultRegexString),
"""
用户名正则,
指定允许在用户名中使用的字符.
默认: %s""".formatted(defaultRegexString)));
if (!regexString.isBlank()) {
try {
usernameRegex = Pattern.compile(regexString);
} catch (Exception e) {
LeafConfig.LOGGER.error("Invalid username regex {} found, falling back to default.", regexString, e);
}
}
if (useUsernameRegex && removeAllCheck) {
LeafConfig.LOGGER.warn("Found conflicting configuration, remove-all-check and use-username-regex cannot be enabled at same time, ignoring remove-all-check...");
removeAllCheck = false;
}
}
}