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 ab8d69fa54203175760d394a5425601073fcd0d9..6d5beffa1a771182d717f2baab5c626b4970c203 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 7de3bbecea2f11e4e1cb65408729f346760dc9b8..b72b81849c7fedad8efc5c7b112e89eb01dc74dd 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 134d74d8ed2c61b28cda0bd5107dea88bc114f51..0392962ed409e3f970904de05926c356610f0e4c 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..1cee7f26a5cd55b146b89da5912952d16269d77e 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..c0329b8304cce7ecfe53b102027f73bf4bbf64de 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;
|
|
}
|