Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f2d3761
应该总是使用 UTF-8 编码以保证内容编码统一
SNWCreations Jan 7, 2025
b632b54
现在支持直接转换文件夹格式的枪包
SNWCreations Jan 7, 2025
54da566
在文件操作器上添加调试信息
SNWCreations Jan 7, 2025
be3938d
应该使用平台指定的路径分隔符
SNWCreations Jan 7, 2025
c6d140c
visitFile 默认应该返回 false
SNWCreations Jan 7, 2025
89f2fd0
display, index, data 属于子目录,单独讨论
SNWCreations Jan 7, 2025
2dc11b3
新的子目录格式颠倒了文件夹名称的顺序
SNWCreations Jan 7, 2025
5e0d11a
参数名称歧义引起的逻辑错误
SNWCreations Jan 7, 2025
9d277c7
旧枪包的元数据文件是 pack.json 而不是 info.json
SNWCreations Jan 7, 2025
49936bc
先复制,再更新
SNWCreations Jan 7, 2025
2d49c10
Writer 没有被 GSON 自动关闭,需要手动关闭
SNWCreations Jan 7, 2025
57b8b81
在新的包位置存在时不要转换
SNWCreations Jan 7, 2025
e11e672
在离开文件夹时应该也检查是否为空
SNWCreations Jan 7, 2025
858abc0
FileOp.andThen 方法产生的实例应该也提供调试信息
SNWCreations Jan 7, 2025
0daa5b4
匿名移动文件用的 FileOp 实例应该也提供调试信息
SNWCreations Jan 7, 2025
59f3b9f
清理不必要的 throws 声明
SNWCreations Jan 7, 2025
f1c46d9
对于非枪包元素,原样复制
SNWCreations Jan 8, 2025
8721419
对于枪包内容,不验证其路径的有效性
SNWCreations Jan 8, 2025
5ea4c39
忽略默认枪包
SNWCreations Jan 8, 2025
71d82f4
读入内容后应该关闭 Reader
SNWCreations Jan 8, 2025
2cba691
允许忽略特定枪包的升级
SNWCreations Jan 8, 2025
4d6edad
简化语法
SNWCreations Jan 8, 2025
6987743
异步运行升级,避免阻塞主线程
SNWCreations Jan 9, 2025
4f26c0a
不正确的相对路径计算方法
SNWCreations Jan 9, 2025
1d0ed19
音效文件夹被错误地移动到数据文件夹
SNWCreations Jan 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/main/java/com/tacz/guns/command/sub/ConvertCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;

import java.util.concurrent.CompletableFuture;

public class ConvertCommand {
private static final String CONVERT_NAME = "convert";

Expand All @@ -19,7 +21,9 @@ public static LiteralArgumentBuilder<CommandSourceStack> get() {
}

private static int convert(CommandContext<CommandSourceStack> context) {
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> PackConvertor.convert(context.getSource()));
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> CompletableFuture.runAsync(
() -> PackConvertor.convert(context.getSource())
));
return Command.SINGLE_SUCCESS;
}
}
10 changes: 10 additions & 0 deletions src/main/java/com/tacz/guns/config/common/OtherConfig.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package com.tacz.guns.config.common;

import net.minecraft.Util;
import net.minecraftforge.common.ForgeConfigSpec;

import java.util.ArrayList;
import java.util.List;

public class OtherConfig {
public static ForgeConfigSpec.BooleanValue DEFAULT_PACK_DEBUG;
public static ForgeConfigSpec.IntValue TARGET_SOUND_DISTANCE;
public static ForgeConfigSpec.DoubleValue SERVER_HITBOX_OFFSET;
public static ForgeConfigSpec.BooleanValue SERVER_HITBOX_LATENCY_FIX;
public static ForgeConfigSpec.DoubleValue SERVER_HITBOX_LATENCY_MAX_SAVE_MS;
public static ForgeConfigSpec.ConfigValue<List<? extends String>> PACK_UPGRADE_BLACKLIST;

public static void init(ForgeConfigSpec.Builder builder) {
builder.push("other");
Expand All @@ -19,6 +24,11 @@ public static void init(ForgeConfigSpec.Builder builder) {
builder.comment("The farthest sound distance of the target, including minecarts type");
TARGET_SOUND_DISTANCE = builder.defineInRange("TargetSoundDistance", 128, 0, Integer.MAX_VALUE);

builder.comment("The pack names in the list will be ignored during legacy pack upgrade");
PACK_UPGRADE_BLACKLIST = builder.defineList("PackUpgradeBlackList",
Util.make(new ArrayList<>(), list -> list.add("tacz_default_gun")),
String.class::isInstance);

serverConfig(builder);

builder.pop();
Expand Down
68 changes: 44 additions & 24 deletions src/main/java/com/tacz/guns/resource/PackConvertor.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import com.google.gson.*;
import com.tacz.guns.GunMod;
import com.tacz.guns.client.resource.pojo.PackInfo;
import com.tacz.guns.config.common.OtherConfig;
import com.tacz.guns.resource.convert.folder.FolderPackConverter;
import com.tacz.guns.util.ThrowingRunnable;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.Component;
import net.minecraftforge.fml.loading.FMLPaths;
Expand All @@ -21,6 +24,8 @@
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import static com.tacz.guns.resource.convert.ConverterUtils.relativePath;

public class PackConvertor {
public static final Path FOLDER = Paths.get("config", GunMod.MOD_ID, "custom");
public static final Pattern PACK_INFO_PATTERN = Pattern.compile("^(\\w+)/pack\\.json$");
Expand Down Expand Up @@ -49,7 +54,8 @@ public static void convert(CommandSourceStack source) {
}
}

File[] files = FOLDER.toFile().listFiles();
File legacyPackFolderAsFile = FOLDER.toFile();
File[] files = legacyPackFolderAsFile.listFiles();
int cnt = 0;
int skip = 0;
int error = 0;
Expand All @@ -59,33 +65,44 @@ public static void convert(CommandSourceStack source) {
msg(source, Component.translatable("message.tacz.converter.start"));
GunMod.LOGGER.info("Start converting legacy packs...");
for (File file : files) {
if (OtherConfig.PACK_UPGRADE_BLACKLIST.get().contains(relativePath(legacyPackFolderAsFile, file))) {
continue;
}
ThrowingRunnable<Exception> conversionOp;
if (file.isFile() && file.getName().endsWith(".zip")) {
PackConvertor.LegacyPack pack = fromZipFile(file);
if (pack != null) {
msg(source, Component.translatable("message.tacz.converter.pack.start", file.getName()));
GunMod.LOGGER.info("Attempt to converting legacy pack: {}", file.getName());
try {
pack.convert();
} catch (FileAlreadyExistsException e) {
msg(source, Component.translatable("message.tacz.converter.pack.exist"));
GunMod.LOGGER.warn("Target file already exists: {}", file.getName());
skip++;
continue;
} catch (Exception e){
msg(source, Component.translatable("message.tacz.converter.pack.failed", file.getName()));
GunMod.LOGGER.error("Failed to convert legacy pack: {}", file.getName(), e);
error++;
continue;
}
cnt++;
msg(source, Component.translatable("message.tacz.converter.pack.finish", file.getName()));
GunMod.LOGGER.info("Legacy pack converted: {}", file.getName());
if (pack == null) {
GunMod.LOGGER.warn("Skip ZIP archive which is not a gun pack: {}", file.getName());
skip++;
continue;
} else {
conversionOp = pack::convert;
}
} else if (file.isDirectory()) {
conversionOp = () -> FolderPackConverter.INSTANCE.convert(file);
} else {
msg(source, Component.translatable("message.tacz.converter.pack.folder", file.getName()));
GunMod.LOGGER.warn("Skip folder: {}", file.getName());
GunMod.LOGGER.warn("Skip non-pack: {}", file.getName());
skip++;
continue;
}
msg(source, Component.translatable("message.tacz.converter.pack.start", file.getName()));
GunMod.LOGGER.info("Attempt to converting legacy pack: {}", file.getName());
try {
conversionOp.run();
} catch (FileAlreadyExistsException e) {
msg(source, Component.translatable("message.tacz.converter.pack.exist"));
GunMod.LOGGER.warn("Target file already exists: {}", file.getName());
skip++;
continue;
} catch (Exception e) {
msg(source, Component.translatable("message.tacz.converter.pack.failed", file.getName()));
GunMod.LOGGER.error("Failed to convert legacy pack: {}", file.getName(), e);
error++;
continue;
}
cnt++;
msg(source, Component.translatable("message.tacz.converter.pack.finish", file.getName()));
GunMod.LOGGER.info("Legacy pack converted: {} (Is folder: {})", file.getName(), file.isDirectory());
}
}
watch.stop();
Expand Down Expand Up @@ -159,7 +176,7 @@ private boolean parseRecipe(ZipOutputStream newZip, ZipEntry entry, ZipFile oldP
if (object != null) {
object.addProperty("type", "tacz:gun_smith_table_crafting");
newZip.putNextEntry(new ZipEntry(newPath));
newZip.write(GSON.toJson(object).getBytes());
newZip.write(GSON.toJson(object).getBytes(StandardCharsets.UTF_8));
newZip.closeEntry();
}
} catch (JsonParseException e) {
Expand Down Expand Up @@ -311,7 +328,7 @@ private void addMeta(ZipOutputStream newZip) throws IOException {

newZip.putNextEntry(entry);
PackMeta meta = new PackMeta(namespace, null);
newZip.write(GSON.toJson(meta).getBytes());
newZip.write(GSON.toJson(meta).getBytes(StandardCharsets.UTF_8));
newZip.closeEntry();
}

Expand Down Expand Up @@ -345,6 +362,9 @@ public void convert() throws FileAlreadyExistsException {
if (parseTags(newZip, entry, oldPack)) continue;
if (parseRecipe(newZip, entry, oldPack)) continue;
if (parsePackInfo(newZip, entry, oldPack)) continue;
// 如果不是任何 TACZ 枪包元素,按原样复制
// 这个情况为诸如 LICENSE 文件等文档考虑
writeEntry(newZip, entry, oldPack, entry.getName());
}
} catch (IOException e) {
GunMod.LOGGER.warn("Failed to convert pack: {}", file.getName());
Expand Down
36 changes: 36 additions & 0 deletions src/main/java/com/tacz/guns/resource/convert/ConverterUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.tacz.guns.resource.convert;

import net.minecraft.server.packs.PackType;
import org.jetbrains.annotations.Nullable;

import java.io.File;

public final class ConverterUtils {

public static String relativePath(File base, File sub) {
return base.toPath().relativize(sub.toPath()).toString();
}

@Nullable
public static UnsafeResourceLocation parseEntryName(File baseDir, File file) {
String relativePath = relativePath(baseDir, file);
int i = relativePath.indexOf(File.separatorChar);
if (i != -1) {
String namespace = relativePath.substring(0, i);
String entryName = relativePath.substring(i + 1).replace(File.separatorChar, '/');
return new UnsafeResourceLocation(namespace, entryName);
}
return null;
}

public static String toFilePath(String namespace, String path, PackType folderType) {
return folderType.getDirectory() + File.separator + namespace + File.separator + path;
}

public static String toFilePath(UnsafeResourceLocation resourceLocation, PackType folderType) {
return toFilePath(resourceLocation.getNamespace(), resourceLocation.getPath(), folderType);
}

private ConverterUtils() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.tacz.guns.resource.convert;

import java.io.IOException;

public interface PackConverter<T> {
boolean convert(T pack) throws IOException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.tacz.guns.resource.convert;

// 不验证路径是否有效的 ResourceLocation 实现
public record UnsafeResourceLocation(String namespace, String path) {
public String getNamespace() {
return namespace;
}

public String getPath() {
return path;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.tacz.guns.resource.convert.folder;

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

import static org.apache.commons.io.file.PathUtils.isEmptyDirectory;

public final class EmptyFolderRemover extends SimpleFileVisitor<Path> {
private final Path baseDir;

public EmptyFolderRemover(Path baseDir) {
this.baseDir = baseDir;
}

@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
if (!baseDir.equals(dir) && isEmptyDirectory(dir)) {
Files.delete(dir);
return FileVisitResult.SKIP_SUBTREE;
}
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
if (exc != null) {
throw exc;
}
if (!baseDir.equals(dir) && isEmptyDirectory(dir)) {
Files.delete(dir);
}
return FileVisitResult.CONTINUE;
}
}
27 changes: 27 additions & 0 deletions src/main/java/com/tacz/guns/resource/convert/folder/FileOp.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.tacz.guns.resource.convert.folder;

import com.tacz.guns.resource.convert.UnsafeResourceLocation;
import net.minecraft.resources.ResourceLocation;

import java.io.File;
import java.io.IOException;

public interface FileOp {
boolean run(File baseDir, File file, UnsafeResourceLocation fileResourceLocation) throws IOException;

default FileOp andThen(FileOp op) {
return new FileOp() {
@Override
public boolean run(File baseDir, File file, UnsafeResourceLocation fileResourceLocation) throws IOException {
boolean self = FileOp.this.run(baseDir, file, fileResourceLocation);
boolean after = op.run(baseDir, file, fileResourceLocation);
return self && after;
}

@Override
public String toString() {
return FileOp.this + ", and then " + op.toString();
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.tacz.guns.resource.convert.folder;

import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;

public interface FolderEntryVisitor {
default boolean visitFile(File baseDir, File subFile) throws IOException {
return false;
}

// return null if you can't handle the given directory
@Nullable
default FileVisitResult visitDirectory(File baseDir, File subFolder) throws IOException {
return null;
}
}
Loading