diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index 87d00a5c0..7349c027d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -60,6 +60,7 @@ import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.event.EventTrigger; import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.plugin.network.*; import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; @@ -1239,17 +1240,17 @@ public class PacketConsumers { try { BukkitServerPlayer player = (BukkitServerPlayer) user; String name = (String) NetworkReflections.methodHandle$ServerboundHelloPacket$nameGetter.invokeExact(packet); - player.setName(name); + player.setUnverifiedName(name); if (VersionHelper.isOrAbove1_20_2()) { UUID uuid = (UUID) NetworkReflections.methodHandle$ServerboundHelloPacket$uuidGetter.invokeExact(packet); - player.setUUID(uuid); + player.setUnverifiedUUID(uuid); } else { @SuppressWarnings("unchecked") Optional uuid = (Optional) NetworkReflections.methodHandle$ServerboundHelloPacket$uuidGetter.invokeExact(packet); if (uuid.isPresent()) { - player.setUUID(uuid.get()); + player.setUnverifiedUUID(uuid.get()); } else { - player.setUUID(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(StandardCharsets.UTF_8))); + player.setUnverifiedUUID(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(StandardCharsets.UTF_8))); } } } catch (Throwable e) { @@ -2499,6 +2500,18 @@ public class PacketConsumers { // 防止后续加入的JoinWorldTask再次处理 user.setShouldProcessFinishConfiguration(false); + // 检查用户UUID是否已经校验 + if (!user.isVerifiedUUID()) { + if (Config.strictPlayerUuidValidation()) { + TranslationManager.instance().log("warning.network.resource_pack.unverified_uuid", user.name(), user.uuid().toString()); + user.kick(Component.translatable("disconnect.loginFailed")); + return; + } + if (Config.debugResourcePack()) { + TranslationManager.instance().log("warning.network.resource_pack.unverified_uuid", user.name(), user.uuid().toString()); + } + } + // 取消 ClientboundFinishConfigurationPacket,让客户端发呆,并结束掉当前的进入世界任务 event.setCancelled(true); try { @@ -2548,8 +2561,8 @@ public class PacketConsumers { public static final TriConsumer LOGIN_FINISHED = (user, event, packet) -> { try { GameProfile gameProfile = FastNMS.INSTANCE.field$ClientboundLoginFinishedPacket$gameProfile(packet); - user.setName(gameProfile.getName()); - user.setUUID(gameProfile.getId()); + user.setVerifiedName(gameProfile.getName()); + user.setVerifiedUUID(gameProfile.getId()); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundLoginFinishedPacket", e); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index c7367b2cf..ddae03bbd 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -72,6 +72,8 @@ public class BukkitServerPlayer extends Player { private ChannelHandler connection; private String name; private UUID uuid; + private boolean isVerifiedName; + private boolean isVerifiedUUID; private ConnectionState decoderState; private ConnectionState encoderState; private boolean shouldProcessFinishConfiguration = true; @@ -140,7 +142,9 @@ public class BukkitServerPlayer extends Player { this.playerRef = new WeakReference<>(player); this.serverPlayerRef = new WeakReference<>(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player)); this.uuid = player.getUniqueId(); + this.isVerifiedUUID = true; this.name = player.getName(); + this.isVerifiedName = true; byte[] bytes = player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(CooldownData.COOLDOWN_KEY), PersistentDataType.BYTE_ARRAY); this.trackedChunks = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(768, 0.5f); this.entityTypeView = new ConcurrentHashMap<>(256); @@ -321,22 +325,46 @@ public class BukkitServerPlayer extends Player { } @Override - public void setName(String name) { - if (this.name != null) return; + public boolean isVerifiedName() { + return this.isVerifiedName; + } + + @Override + public void setUnverifiedName(String name) { + if (this.isVerifiedName) return; this.name = name; } + @Override + public void setVerifiedName(String name) { + if (this.isVerifiedName) return; + this.name = name; + this.isVerifiedName = true; + } + @Override public UUID uuid() { return this.uuid; } @Override - public void setUUID(UUID uuid) { - if (this.uuid != null) return; + public boolean isVerifiedUUID() { + return this.isVerifiedUUID; + } + + @Override + public void setUnverifiedUUID(UUID uuid) { + if (this.isVerifiedUUID) return; this.uuid = uuid; } + @Override + public void setVerifiedUUID(UUID uuid) { + if (this.isVerifiedUUID) return; + this.uuid = uuid; + this.isVerifiedUUID = true; + } + @Override public void playSound(Key sound, SoundSource source, float volume, float pitch) { platformPlayer().playSound(platformPlayer(), sound.toString(), SoundUtils.toBukkit(source), volume, pitch); diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index 2c0c66aad..cb1226ba1 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -98,6 +98,8 @@ resource-pack: file-to-upload: "./generated/resource_pack.zip" # Resend the resource pack to players upon successful upload resend-on-upload: true + # Whether a verified player UUID is required to get the resource pack + strict-player-uuid-validation: true duplicated-files-handler: - term: type: any_of diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index bde6e388d..885cb3fbc 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -63,7 +63,8 @@ command.upload.failure.not_supported: "Current hosting method '' doe command.upload.on_progress: "Started uploading progress. Check the console for more information." command.send_resource_pack.success.single: "Sent resource pack to ." command.send_resource_pack.success.multiple: "Send resource packs to players." -warning.config.pack.duplicated_files: "Duplicated files Found. Please resolve them through config.yml 'resource-pack.duplicated-files-handler' section." +warning.network.resource_pack.unverified_uuid: "Player attempts to request a resource package using a UUID () that is not authenticated by the server." +warning.config.pack.duplicated_files: "Duplicated files Found. Please resolve them through config.yml 'resource-pack.duplicated-files-handler' section." warning.config.yaml.duplicated_key: "Issue found in file - Found duplicated key '' at line , this might cause unexpected results." warning.config.yaml.inconsistent_value_type: "Issue found in file - Found duplicated key '' at line with different value types, this might cause unexpected results." warning.config.type.int: "Issue found in file - Failed to load '': Cannot cast '' to integer type for option ''." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index b1f2fb30d..72fb2c989 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -63,7 +63,8 @@ command.upload.failure.not_supported: "当前托管模式 '' 不支 command.upload.on_progress: "已开始上传进程. 检查控制台以获取详细信息" command.send_resource_pack.success.single: "发送资源包给 " command.send_resource_pack.success.multiple: "发送资源包给 个玩家" -warning.config.pack.duplicated_files: "发现重复文件 请通过 config.yml 的 'resource-pack.duplicated-files-handler' 部分解决" +warning.network.resource_pack.unverified_uuid: "玩家 使用未经服务器验证的 UUID () 尝试请求获取资源包" +warning.config.pack.duplicated_files: "发现重复文件 请通过 config.yml 的 'resource-pack.duplicated-files-handler' 部分解决" warning.config.yaml.duplicated_key: "在文件 发现问题 - 在第行发现重复的键 '', 这可能会导致一些意料之外的问题" warning.config.yaml.inconsistent_value_type: "在文件 发现问题 - 在第行发现重复且值类型不同的键 '', 这可能会导致一些意料之外的问题" warning.config.type.int: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为整数类型 (选项 '')" diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java index 922cd4286..ffdc1cacf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java @@ -93,6 +93,7 @@ public class Config { protected boolean resource_pack$delivery$send_on_join; protected boolean resource_pack$delivery$resend_on_upload; protected boolean resource_pack$delivery$auto_upload; + protected boolean resource_pack$delivery$strict_player_uuid_validation; protected Path resource_pack$delivery$file_to_upload; protected Component resource_pack$send$prompt; @@ -271,6 +272,7 @@ public class Config { resource_pack$delivery$kick_if_declined = config.getBoolean("resource-pack.delivery.kick-if-declined", true); resource_pack$delivery$kick_if_failed_to_apply = config.getBoolean("resource-pack.delivery.kick-if-failed-to-apply", true); resource_pack$delivery$auto_upload = config.getBoolean("resource-pack.delivery.auto-upload", true); + resource_pack$delivery$strict_player_uuid_validation = config.getBoolean("resource-pack.delivery.strict-player-uuid-validation", true); resource_pack$delivery$file_to_upload = resolvePath(config.getString("resource-pack.delivery.file-to-upload", "./generated/resource_pack.zip")); resource_pack$send$prompt = AdventureHelper.miniMessage().deserialize(config.getString("resource-pack.delivery.prompt", "To fully experience our server, please accept our custom resource pack.")); resource_pack$protection$crash_tools$method_1 = config.getBoolean("resource-pack.protection.crash-tools.method-1", false); @@ -590,6 +592,9 @@ public class Config { public static boolean autoUpload() { return instance.resource_pack$delivery$auto_upload; } + public static boolean strictPlayerUuidValidation() { + return instance.resource_pack$delivery$strict_player_uuid_validation; + } public static Path fileToUpload() { return instance.resource_pack$delivery$file_to_upload; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java index eaf72bf24..00d359c5f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java @@ -31,11 +31,19 @@ public interface NetWorkUser { String name(); - void setName(String name); + boolean isVerifiedName(); + + void setUnverifiedName(String name); + + void setVerifiedName(String name); UUID uuid(); - void setUUID(UUID uuid); + boolean isVerifiedUUID(); + + void setUnverifiedUUID(UUID uuid); + + void setVerifiedUUID(UUID uuid); void sendPacket(Object packet, boolean immediately);