9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-19 15:09:15 +00:00

添加指令用于修改结构nbt文件

This commit is contained in:
XiaoMoMi
2025-12-09 02:01:36 +08:00
parent 4ce4812523
commit da2f25cde5
11 changed files with 217 additions and 8 deletions

View File

@@ -1,5 +1,5 @@
plugins { plugins {
id("com.gradleup.shadow") version "9.2.2" id("com.gradleup.shadow") version "9.3.0"
id("maven-publish") id("maven-publish")
} }

View File

@@ -1,5 +1,5 @@
plugins { plugins {
id("com.gradleup.shadow") version "9.2.2" id("com.gradleup.shadow") version "9.3.0"
} }
repositories { repositories {

View File

@@ -1,5 +1,5 @@
plugins { 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" id("de.eldoria.plugin-yml.bukkit") version "0.7.1"
} }

View File

@@ -4,7 +4,7 @@ import xyz.jpenilla.runtask.pluginsapi.DownloadPluginsSpec
import java.net.URI import java.net.URI
plugins { 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("de.eldoria.plugin-yml.paper") version "0.7.1"
id("xyz.jpenilla.run-paper") version "3.0.2" id("xyz.jpenilla.run-paper") version "3.0.2"
} }

View File

@@ -60,6 +60,7 @@ public class BukkitCommandManager extends AbstractCommandManager<CommandSender>
new DebugIsSectionInjectedCommand(this, plugin), new DebugIsSectionInjectedCommand(this, plugin),
new DebugMigrateTemplatesCommand(this, plugin), new DebugMigrateTemplatesCommand(this, plugin),
new DebugIsChunkPersistentLoadedCommand(this, plugin), new DebugIsChunkPersistentLoadedCommand(this, plugin),
new DebugOptimizeFurnitureStructureCommand(this, plugin),
new TotemAnimationCommand(this, plugin), new TotemAnimationCommand(this, plugin),
new EnableResourceCommand(this, plugin), new EnableResourceCommand(this, plugin),
new DisableResourceCommand(this, plugin), new DisableResourceCommand(this, plugin),

View File

@@ -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<CommandSender> {
public DebugOptimizeFurnitureStructureCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
super(commandManager, plugin);
}
@Override
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
return builder
.required("world", WorldParser.worldParser())
.required("file", NamespacedKeyParser.namespacedKeyComponent().suggestionProvider(new SuggestionProvider<>() {
@Override
public @NonNull CompletableFuture<? extends @NonNull Iterable<? extends @NonNull Suggestion>> suggestionsFuture(@NonNull CommandContext<Object> 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<String> findStructures(Path startPath) throws IOException {
// 并行遍历文件树使用自定义的BiPredicate进行过滤
try (Stream<Path> 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<String> 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;
}
}

View File

@@ -283,6 +283,13 @@ debug_furniture:
- /craftengine debug furniture - /craftengine debug furniture
- /ce 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: debug_test:
enable: true enable: true
permission: ce.command.debug.test permission: ce.command.debug.test

View File

@@ -1,5 +1,5 @@
plugins { plugins {
id("com.gradleup.shadow") version "9.2.2" id("com.gradleup.shadow") version "9.3.0"
id("maven-publish") id("maven-publish")
} }

View File

@@ -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);
}
}
}

View File

@@ -37,7 +37,7 @@ geantyref_version=1.3.16
zstd_version=1.5.7-6 zstd_version=1.5.7-6
commons_io_version=2.21.0 commons_io_version=2.21.0
commons_lang3_version=3.20.0 commons_lang3_version=3.20.0
sparrow_nbt_version=0.10.8 sparrow_nbt_version=0.10.9
sparrow_util_version=0.69 sparrow_util_version=0.69
fastutil_version=8.5.18 fastutil_version=8.5.18
netty_version=4.1.128.Final netty_version=4.1.128.Final
@@ -48,7 +48,7 @@ byte_buddy_version=1.18.1
ahocorasick_version=0.6.3 ahocorasick_version=0.6.3
snake_yaml_version=2.5 snake_yaml_version=2.5
anti_grief_version=1.0.5 anti_grief_version=1.0.5
nms_helper_version=1.0.148 nms_helper_version=1.0.149
evalex_version=3.5.0 evalex_version=3.5.0
reactive_streams_version=1.0.4 reactive_streams_version=1.0.4
amazon_awssdk_version=2.38.7 amazon_awssdk_version=2.38.7

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists 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 networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists