Files
Shiroha/shiroha-server/minecraft-patches/features/0067-Leaf-Configurable-vanilla-username-check.patch
T
NanaChiyo0721 1bb895c165
Shiroha CI / build (push) Canceled after 0s
Shiroha CI / Event File (push) Canceled after 0s
Fix incorrect ticket lock size computing
Yeah this would fully solve the issue of chunk unloading race condition

The same fix was already initially added at "Force clamp grid-exponent to 1~6"
2026-08-09 14:57:32 +08:00

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 c4dddd5c66098301de2abc2de93d4409cd14531d..24ccb47605427f83bbfe552edf4d92e9c16e2a1f 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 1b4229085f9d4189efb20c083bbfe2e6712e72a4..822e69a975b5c08547b17f8abfa0d512c70a2625 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 (io.nanachiyo0721.shiroha.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 (!io.nanachiyo0721.shiroha.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 (io.nanachiyo0721.shiroha.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 7ee90d4a8d333c253077871cede3fed63ef23539..dffb0a5cbbcf7a225402bd2e364941b5271a2c24 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 (io.nanachiyo0721.shiroha.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..3d87f68561a08bc64d3f5a6efb6498984fc84250 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, io.nanachiyo0721.shiroha.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 (io.nanachiyo0721.shiroha.config.modules.misc.UsernameCheckConfig.allowOldPlayersJoin && net.minecraft.server.MinecraftServer.getServer().getPlayerList().playedPlayers.contains(name)) return true;
+ if (io.nanachiyo0721.shiroha.config.modules.misc.UsernameCheckConfig.useCustomUsernameRegex()) {
+ return io.nanachiyo0721.shiroha.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..36fc92246c4afce7804eb013829a41fbda3610f4 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 io.nanachiyo0721.shiroha.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 io.nanachiyo0721.shiroha.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 io.nanachiyo0721.shiroha.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;
}