From da2f25cde55250fa3bb4fa3f16693960c2594a76 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Tue, 9 Dec 2025 02:01:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=8C=87=E4=BB=A4=E7=94=A8?= =?UTF-8?q?=E4=BA=8E=E4=BF=AE=E6=94=B9=E7=BB=93=E6=9E=84nbt=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bukkit/build.gradle.kts | 2 +- bukkit/legacy/build.gradle.kts | 2 +- bukkit/loader/build.gradle.kts | 2 +- bukkit/paper-loader/build.gradle.kts | 2 +- .../plugin/command/BukkitCommandManager.java | 1 + ...ebugOptimizeFurnitureStructureCommand.java | 175 ++++++++++++++++++ common-files/src/main/resources/commands.yml | 7 + core/build.gradle.kts | 2 +- .../craftengine/core/util/NBTUtils.java | 26 +++ gradle.properties | 4 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 11 files changed, 217 insertions(+), 8 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugOptimizeFurnitureStructureCommand.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/NBTUtils.java diff --git a/bukkit/build.gradle.kts b/bukkit/build.gradle.kts index f532acde6..10489df7f 100644 --- a/bukkit/build.gradle.kts +++ b/bukkit/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.gradleup.shadow") version "9.2.2" + id("com.gradleup.shadow") version "9.3.0" id("maven-publish") } diff --git a/bukkit/legacy/build.gradle.kts b/bukkit/legacy/build.gradle.kts index 75ecb2c84..0196a21b3 100644 --- a/bukkit/legacy/build.gradle.kts +++ b/bukkit/legacy/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.gradleup.shadow") version "9.2.2" + id("com.gradleup.shadow") version "9.3.0" } repositories { diff --git a/bukkit/loader/build.gradle.kts b/bukkit/loader/build.gradle.kts index 901f5444a..b8d526844 100644 --- a/bukkit/loader/build.gradle.kts +++ b/bukkit/loader/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.gradleup.shadow") version "9.2.2" + id("com.gradleup.shadow") version "9.3.0" id("de.eldoria.plugin-yml.bukkit") version "0.7.1" } diff --git a/bukkit/paper-loader/build.gradle.kts b/bukkit/paper-loader/build.gradle.kts index 41bb27752..71d886cdf 100644 --- a/bukkit/paper-loader/build.gradle.kts +++ b/bukkit/paper-loader/build.gradle.kts @@ -4,7 +4,7 @@ import xyz.jpenilla.runtask.pluginsapi.DownloadPluginsSpec import java.net.URI plugins { - id("com.gradleup.shadow") version "9.2.2" + id("com.gradleup.shadow") version "9.3.0" id("de.eldoria.plugin-yml.paper") version "0.7.1" id("xyz.jpenilla.run-paper") version "3.0.2" } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitCommandManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitCommandManager.java index 5f44c5689..3ea7fa6a2 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitCommandManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitCommandManager.java @@ -60,6 +60,7 @@ public class BukkitCommandManager extends AbstractCommandManager new DebugIsSectionInjectedCommand(this, plugin), new DebugMigrateTemplatesCommand(this, plugin), new DebugIsChunkPersistentLoadedCommand(this, plugin), + new DebugOptimizeFurnitureStructureCommand(this, plugin), new TotemAnimationCommand(this, plugin), new EnableResourceCommand(this, plugin), new DisableResourceCommand(this, plugin), diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugOptimizeFurnitureStructureCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugOptimizeFurnitureStructureCommand.java new file mode 100644 index 000000000..e3ff5fce1 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugOptimizeFurnitureStructureCommand.java @@ -0,0 +1,175 @@ +package net.momirealms.craftengine.bukkit.plugin.command.feature; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; +import net.momirealms.craftengine.core.plugin.command.sender.Sender; +import net.momirealms.craftengine.core.util.NBTUtils; +import net.momirealms.sparrow.nbt.CompoundTag; +import net.momirealms.sparrow.nbt.DoubleTag; +import net.momirealms.sparrow.nbt.ListTag; +import net.momirealms.sparrow.nbt.Tag; +import org.bukkit.NamespacedKey; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.Command; +import org.incendo.cloud.bukkit.parser.NamespacedKeyParser; +import org.incendo.cloud.bukkit.parser.WorldParser; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.parser.standard.DoubleParser; +import org.incendo.cloud.suggestion.Suggestion; +import org.incendo.cloud.suggestion.SuggestionProvider; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.FileVisitOption; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class DebugOptimizeFurnitureStructureCommand extends BukkitCommandFeature { + + public DebugOptimizeFurnitureStructureCommand(CraftEngineCommandManager commandManager, CraftEngine plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { + return builder + .required("world", WorldParser.worldParser()) + .required("file", NamespacedKeyParser.namespacedKeyComponent().suggestionProvider(new SuggestionProvider<>() { + @Override + public @NonNull CompletableFuture> suggestionsFuture(@NonNull CommandContext context, @NonNull CommandInput input) { + World world = context.get("world"); + Path generated = world.getWorldFolder().toPath().resolve("generated"); + if (!Files.exists(generated)) { + return CompletableFuture.completedFuture(Collections.emptyList()); + } + try { + return CompletableFuture.completedFuture(findStructures(generated).stream().map(Suggestion::suggestion).collect(Collectors.toList())); + } catch (IOException e) { + return CompletableFuture.completedFuture(Collections.emptyList()); + } + } + })) + .optional("y-offset", DoubleParser.doubleParser()) + .handler(context -> { + World world = context.get("world"); + NamespacedKey file = context.get("file"); + double offset = (double) context.optional("y-offset").orElse(0d); + Path filePath = world.getWorldFolder().toPath().resolve("generated").resolve(file.getNamespace()).resolve("structures").resolve(file.getKey() + ".nbt"); + Sender sender = plugin().senderFactory().wrap(context.sender()); + if (!Files.exists(filePath)) { + sender.sendMessage(Component.text("File not found", NamedTextColor.RED)); + return; + } + try (InputStream is = Files.newInputStream(filePath)) { + CompoundTag structureTag = NBTUtils.readCompressed(is); + ListTag entitiesTag = structureTag.getList("entities"); + if (entitiesTag == null) { + sender.sendMessage(Component.text("Entities not found", NamedTextColor.RED)); + return; + } + int count = 0; + ListTag toSave = new ListTag(); + for (Tag entityTag0 : entitiesTag) { + CompoundTag entityTag = (CompoundTag) entityTag0; + CompoundTag entityNBTTag = (CompoundTag) entityTag.get("nbt"); + if (entityNBTTag != null) { + CompoundTag bukkitValuesTag = (CompoundTag) entityNBTTag.get("BukkitValues"); + if (bukkitValuesTag != null) { + // 不保存碰撞实体 + if (bukkitValuesTag.containsKey("craftengine:collision")) { + count++; + continue; + } + if (bukkitValuesTag.containsKey("craftengine:furniture_id")) { + if (offset != 0) { + ListTag pos = (ListTag) entityTag.get("pos"); + double previousY = pos.getDouble(1); + pos.set(1, new DoubleTag(previousY + offset)); + count++; + } + } + } + } + toSave.add(entityTag); + } + if (count == 0) { + sender.sendMessage(Component.text("Nothing changed", NamedTextColor.WHITE)); + } else { + structureTag.put("entities", toSave); + try (OutputStream os = Files.newOutputStream(filePath)) { + NBTUtils.writeCompressed(structureTag, os); + } catch (IOException e) { + sender.sendMessage(Component.text("Internal error", NamedTextColor.RED)); + plugin().logger().severe("Cannot write structure NBT file", e); + return; + } + sender.sendMessage(Component.text(count + " entities modified", NamedTextColor.WHITE)); + } + } catch (IOException e) { + sender.sendMessage(Component.text("Internal error", NamedTextColor.RED)); + plugin().logger().severe("Cannot read structure NBT file", e); + } + }); + } + + @Override + public String getFeatureID() { + return "debug_optimize_furniture_structure"; + } + + public static List findStructures(Path startPath) throws IOException { + // 并行遍历文件树,使用自定义的BiPredicate进行过滤 + try (Stream stream = Files.walk(startPath, FileVisitOption.FOLLOW_LINKS)) { + return stream.filter(Files::isRegularFile) + .filter(path -> { + String filename = path.getFileName().toString(); + return filename.endsWith(".nbt") && + filename.length() > 4; // 确保有文件名(不只是.nbt) + }) + .map(path -> { + try { + return extractPair(startPath, path); + } catch (Exception e) { + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + } + + private static String extractPair(Path basePath, Path nbtFile) { + try { + Path relativePath = basePath.relativize(nbtFile); + List parts = new ArrayList<>(); + for (Path part : relativePath) { + parts.add(part.toString()); + } + // 检查是否符合 xxx/structures/xxx.nbt 模式 + if (parts.size() >= 3 && "structures".equals(parts.get(parts.size() - 2))) { + String firstXxx = parts.get(parts.size() - 3); + String fileName = parts.getLast(); + if (fileName.endsWith(".nbt")) { + String secondXxx = fileName.substring(0, fileName.length() - 4); + return firstXxx + ":" + secondXxx; + } + } + } catch (Exception ignored) { + } + return null; + } +} diff --git a/common-files/src/main/resources/commands.yml b/common-files/src/main/resources/commands.yml index e9929cd06..26ec2fe89 100644 --- a/common-files/src/main/resources/commands.yml +++ b/common-files/src/main/resources/commands.yml @@ -283,6 +283,13 @@ debug_furniture: - /craftengine debug furniture - /ce debug furniture +debug_optimize_furniture_structure: + enable: true + permission: ce.command.debug.optimize_furniture_structure + usage: + - /craftengine debug optimize-furniture-structure + - /ce debug optimize-furniture-structure + debug_test: enable: true permission: ce.command.debug.test diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 802335f5c..f49d99ae4 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.gradleup.shadow") version "9.2.2" + id("com.gradleup.shadow") version "9.3.0" id("maven-publish") } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/NBTUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/NBTUtils.java new file mode 100644 index 000000000..6c92e63e8 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/NBTUtils.java @@ -0,0 +1,26 @@ +package net.momirealms.craftengine.core.util; + +import net.momirealms.sparrow.nbt.CompoundTag; +import net.momirealms.sparrow.nbt.NBT; + +import java.io.*; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +public final class NBTUtils { + + private NBTUtils() { + } + + public static void writeCompressed(CompoundTag nbt, OutputStream stream) throws IOException { + try (DataOutputStream dataoutputstream = new DataOutputStream(new BufferedOutputStream(new GZIPOutputStream(stream)))) { + NBT.writeCompound(nbt, dataoutputstream, true); + } + } + + public static CompoundTag readCompressed(InputStream stream) throws IOException { + try (DataInputStream datainputstream = new DataInputStream(new BufferedInputStream(new GZIPInputStream(stream)))) { + return NBT.readCompound(datainputstream, true); + } + } +} diff --git a/gradle.properties b/gradle.properties index 4d21c038e..459452d32 100644 --- a/gradle.properties +++ b/gradle.properties @@ -37,7 +37,7 @@ geantyref_version=1.3.16 zstd_version=1.5.7-6 commons_io_version=2.21.0 commons_lang3_version=3.20.0 -sparrow_nbt_version=0.10.8 +sparrow_nbt_version=0.10.9 sparrow_util_version=0.69 fastutil_version=8.5.18 netty_version=4.1.128.Final @@ -48,7 +48,7 @@ byte_buddy_version=1.18.1 ahocorasick_version=0.6.3 snake_yaml_version=2.5 anti_grief_version=1.0.5 -nms_helper_version=1.0.148 +nms_helper_version=1.0.149 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.38.7 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ab521aa07..cd4b7aa89 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists