From 30e665c57682549c4f23be16ec27e07d38265ea8 Mon Sep 17 00:00:00 2001 From: MC_XiaoHei Date: Mon, 7 Jul 2025 15:12:12 +0800 Subject: [PATCH] fix: fakeplayer load by world(#411) --- .../features/0007-Leaves-Fakeplayer.patch | 31 +++++++------ .../features/0015-No-chat-sign.patch | 6 +-- ...19-Config-to-disable-method-profiler.patch | 4 +- .../0038-Catch-update-suppression-crash.patch | 4 +- .../features/0039-Bedrock-break-list.patch | 4 +- .../features/0074-Replay-Mod-API.patch | 4 +- .../features/0075-Leaves-I18n-support.patch | 6 +-- ...r-stand-cant-kill-by-mob-projectile.patch} | 0 ...4-Disable-offline-warn-if-use-proxy.patch} | 4 +- .../0088-More-Region-Format-Support.patch | 4 +- .../features/0005-Leaves-Fakeplayer.patch | 14 +++++- .../leavesmc/leaves/bot/BotDataStorage.java | 12 +++++ .../java/org/leavesmc/leaves/bot/BotList.java | 45 +++++++++++++++---- .../java/org/leavesmc/leaves/bot/BotUtil.java | 13 ++++++ 14 files changed, 107 insertions(+), 44 deletions(-) rename leaves-server/minecraft-patches/features/{0084-Armor-stand-cant-kill-by-mob-projectile.patch => 0083-Armor-stand-cant-kill-by-mob-projectile.patch} (100%) rename leaves-server/minecraft-patches/features/{0083-Disable-offline-warn-if-use-proxy.patch => 0084-Disable-offline-warn-if-use-proxy.patch} (88%) diff --git a/leaves-server/minecraft-patches/features/0007-Leaves-Fakeplayer.patch b/leaves-server/minecraft-patches/features/0007-Leaves-Fakeplayer.patch index 67944b40..f439c7e7 100644 --- a/leaves-server/minecraft-patches/features/0007-Leaves-Fakeplayer.patch +++ b/leaves-server/minecraft-patches/features/0007-Leaves-Fakeplayer.patch @@ -30,7 +30,7 @@ index 8bab2c26e10e8495fd39be470bcb02917fe56f40..c9840dfa4067973aafed7e7c7305182d private DisconnectionDetails disconnectionDetails; private boolean encrypted; diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 95f5d078019ebd4fde9bf65748d866b51cfeabc6..05966b2600bead23ab2ec19c95a3797eda7f3ef8 100644 +index 95f5d078019ebd4fde9bf65748d866b51cfeabc6..feb5fc93498a5ed89e3bcb61cd72ba6e75881358 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -304,6 +304,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop S spin(Function threadFunction) { ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system AtomicReference atomicReference = new AtomicReference<>(); -@@ -740,6 +742,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop= resultLimit) { return players; diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index f288b85909550e6eec931ee2da9e0d4c7808fdc9..f880cfc9394454bc090f41bc51fbc29b382fab31 100644 +index a9322925daca18b3f3120bd540c47b4758b1ecf7..57b0ec8336722197dae868d92d8733330f0b0722 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java -@@ -1640,7 +1640,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop read(String uuid) { + File file = new File(this.botDir, uuid + ".dat"); + if (file.exists() && file.isFile()) { + try { + return Optional.of(NbtIo.readCompressed(file.toPath(), NbtAccounter.unlimitedHeap())); + } catch (Exception exception) { + BotDataStorage.LOGGER.warn("Failed to read fakeplayer data for {}", uuid); + } + } + return Optional.empty(); + } + private void saveBotList() { try { if (this.botListFile.exists() && this.botListFile.isFile()) { diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/bot/BotList.java b/leaves-server/src/main/java/org/leavesmc/leaves/bot/BotList.java index 98fe3805..abf7b14b 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/bot/BotList.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/bot/BotList.java @@ -36,10 +36,12 @@ import org.leavesmc.leaves.event.bot.BotRemoveEvent; import org.leavesmc.leaves.event.bot.BotSpawnLocationEvent; import org.slf4j.Logger; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; @@ -56,6 +58,7 @@ public class BotList { private final Map botsByUUID = Maps.newHashMap(); private final Map botsByName = Maps.newHashMap(); + private final Map> botsNameByWorldUuid = Maps.newHashMap(); public BotList(MinecraftServer server) { this.server = server; @@ -171,6 +174,9 @@ public class BotList { bot.level().getChunkSource().chunkMap.addEntity(bot); bot.initInventoryMenu(); + botsNameByWorldUuid + .computeIfAbsent(bot.level().uuid.toString(), (k) -> new HashSet<>()) + .add(bot.getBukkitEntity().getRealName()); BotList.LOGGER.info("{}[{}] logged in with entity id {} at ([{}]{}, {}, {})", bot.getName().getString(), "Local", bot.getId(), bot.level().serverLevelData.getLevelName(), bot.getX(), bot.getY(), bot.getZ()); return bot; } @@ -198,6 +204,7 @@ public class BotList { playerIO.save(bot); } else { bot.dropAll(true); + botsNameByWorldUuid.get(bot.level().uuid.toString()).remove(bot.getBukkitEntity().getRealName()); } if (bot.isPassenger() && event.shouldSave()) { @@ -250,6 +257,15 @@ public class BotList { return true; } + public void removeAllIn(String worldUuid) { + for (String realName : this.botsNameByWorldUuid.getOrDefault(worldUuid, new HashSet<>())) { + ServerBot bot = this.getBotByName(realName); + if (bot != null) { + this.removeBot(bot, BotRemoveEvent.RemoveReason.INTERNAL, null, LeavesConfig.modify.fakeplayer.canResident); + } + } + } + public void removeAll() { for (ServerBot bot : this.bots) { bot.resume = LeavesConfig.modify.fakeplayer.canResident; @@ -257,18 +273,31 @@ public class BotList { } } - public void loadResume() { - if (LeavesConfig.modify.fakeplayer.enable && LeavesConfig.modify.fakeplayer.canResident) { - CompoundTag savedBotList = this.getSavedBotList().copy(); - for (String realName : savedBotList.keySet()) { - CompoundTag nbt = savedBotList.getCompound(realName).orElseThrow(); - if (nbt.getBoolean("resume").orElse(false)) { - this.loadNewBot(realName); - } + public void loadBotInfo() { + System.out.println(LeavesConfig.modify.fakeplayer.canResident); + if (!LeavesConfig.modify.fakeplayer.enable || !LeavesConfig.modify.fakeplayer.canResident) return; + CompoundTag savedBotList = this.getSavedBotList().copy(); + for (String realName : savedBotList.keySet()) { + CompoundTag nbt = savedBotList.getCompound(realName).orElseThrow(); + if (!nbt.getBoolean("resume").orElse(false)) continue; + UUID levelUuid = BotUtil.getBotLevel(realName, this.dataStorage); + if (levelUuid == null) { + LOGGER.warn("Bot {} has no world UUID, skipping loading.", realName); + continue; } + this.botsNameByWorldUuid + .computeIfAbsent(levelUuid.toString(), (k) -> new HashSet<>()) + .add(realName); } } + public void loadResume(String worldUuid) { + if (!LeavesConfig.modify.fakeplayer.enable || !LeavesConfig.modify.fakeplayer.canResident) return; + Set bots = this.botsNameByWorldUuid.get(worldUuid); + if (bots == null) return; + bots.forEach(this::loadNewBot); + } + public void networkTick() { this.bots.forEach(ServerBot::networkTick); } diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/bot/BotUtil.java b/leaves-server/src/main/java/org/leavesmc/leaves/bot/BotUtil.java index 6504fa29..266ec737 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/bot/BotUtil.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/bot/BotUtil.java @@ -2,12 +2,14 @@ package org.leavesmc.leaves.bot; import com.google.common.base.Charsets; import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.item.ItemStack; import org.bukkit.Bukkit; import org.jetbrains.annotations.NotNull; import org.leavesmc.leaves.LeavesConfig; +import java.util.Optional; import java.util.UUID; public class BotUtil { @@ -73,6 +75,17 @@ public class BotUtil { return UUID.nameUUIDFromBytes(("Fakeplayer:" + realName).getBytes(Charsets.UTF_8)); } + public static UUID getBotLevel(@NotNull String realName, BotDataStorage botDataStorage) { + UUID uuid = BotUtil.getBotUUID(realName); + Optional tagOptional = botDataStorage.read(uuid.toString()); + if (tagOptional.isEmpty()) return null; + CompoundTag tag = tagOptional.get(); + Optional worldUUIDMost = tag.getLong("WorldUUIDMost"); + Optional worldUUIDLeast = tag.getLong("WorldUUIDLeast"); + if (worldUUIDMost.isEmpty() || worldUUIDLeast.isEmpty()) return null; + return new UUID(worldUUIDMost.get(), worldUUIDLeast.get()); + } + public static String getFullName(String inputName) { return LeavesConfig.modify.fakeplayer.prefix + inputName + LeavesConfig.modify.fakeplayer.suffix; }