diff --git a/src/main/java/com/tacz/guns/command/sub/ConvertCommand.java b/src/main/java/com/tacz/guns/command/sub/ConvertCommand.java index b087dbc94..14eb3bc94 100644 --- a/src/main/java/com/tacz/guns/command/sub/ConvertCommand.java +++ b/src/main/java/com/tacz/guns/command/sub/ConvertCommand.java @@ -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"; @@ -19,7 +21,9 @@ public static LiteralArgumentBuilder get() { } private static int convert(CommandContext context) { - DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> PackConvertor.convert(context.getSource())); + DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> CompletableFuture.runAsync( + () -> PackConvertor.convert(context.getSource()) + )); return Command.SINGLE_SUCCESS; } } diff --git a/src/main/java/com/tacz/guns/config/common/OtherConfig.java b/src/main/java/com/tacz/guns/config/common/OtherConfig.java index 1737247fd..361e036a5 100644 --- a/src/main/java/com/tacz/guns/config/common/OtherConfig.java +++ b/src/main/java/com/tacz/guns/config/common/OtherConfig.java @@ -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> PACK_UPGRADE_BLACKLIST; public static void init(ForgeConfigSpec.Builder builder) { builder.push("other"); @@ -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(); diff --git a/src/main/java/com/tacz/guns/resource/PackConvertor.java b/src/main/java/com/tacz/guns/resource/PackConvertor.java index 9e4cd3703..f998b549b 100644 --- a/src/main/java/com/tacz/guns/resource/PackConvertor.java +++ b/src/main/java/com/tacz/guns/resource/PackConvertor.java @@ -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; @@ -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$"); @@ -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; @@ -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 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(); @@ -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) { @@ -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(); } @@ -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()); diff --git a/src/main/java/com/tacz/guns/resource/convert/ConverterUtils.java b/src/main/java/com/tacz/guns/resource/convert/ConverterUtils.java new file mode 100644 index 000000000..ae8a49463 --- /dev/null +++ b/src/main/java/com/tacz/guns/resource/convert/ConverterUtils.java @@ -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() { + } +} diff --git a/src/main/java/com/tacz/guns/resource/convert/PackConverter.java b/src/main/java/com/tacz/guns/resource/convert/PackConverter.java new file mode 100644 index 000000000..53b947a37 --- /dev/null +++ b/src/main/java/com/tacz/guns/resource/convert/PackConverter.java @@ -0,0 +1,7 @@ +package com.tacz.guns.resource.convert; + +import java.io.IOException; + +public interface PackConverter { + boolean convert(T pack) throws IOException; +} diff --git a/src/main/java/com/tacz/guns/resource/convert/UnsafeResourceLocation.java b/src/main/java/com/tacz/guns/resource/convert/UnsafeResourceLocation.java new file mode 100644 index 000000000..164b3a982 --- /dev/null +++ b/src/main/java/com/tacz/guns/resource/convert/UnsafeResourceLocation.java @@ -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; + } +} diff --git a/src/main/java/com/tacz/guns/resource/convert/folder/EmptyFolderRemover.java b/src/main/java/com/tacz/guns/resource/convert/folder/EmptyFolderRemover.java new file mode 100644 index 000000000..58e117437 --- /dev/null +++ b/src/main/java/com/tacz/guns/resource/convert/folder/EmptyFolderRemover.java @@ -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 { + 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; + } +} diff --git a/src/main/java/com/tacz/guns/resource/convert/folder/FileOp.java b/src/main/java/com/tacz/guns/resource/convert/folder/FileOp.java new file mode 100644 index 000000000..c11661f12 --- /dev/null +++ b/src/main/java/com/tacz/guns/resource/convert/folder/FileOp.java @@ -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(); + } + }; + } +} diff --git a/src/main/java/com/tacz/guns/resource/convert/folder/FolderEntryVisitor.java b/src/main/java/com/tacz/guns/resource/convert/folder/FolderEntryVisitor.java new file mode 100644 index 000000000..76b6596d9 --- /dev/null +++ b/src/main/java/com/tacz/guns/resource/convert/folder/FolderEntryVisitor.java @@ -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; + } +} diff --git a/src/main/java/com/tacz/guns/resource/convert/folder/FolderPackConverter.java b/src/main/java/com/tacz/guns/resource/convert/folder/FolderPackConverter.java new file mode 100644 index 000000000..90edc3d4d --- /dev/null +++ b/src/main/java/com/tacz/guns/resource/convert/folder/FolderPackConverter.java @@ -0,0 +1,295 @@ +package com.tacz.guns.resource.convert.folder; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.tacz.guns.GunMod; +import com.tacz.guns.client.resource.pojo.PackInfo; +import com.tacz.guns.resource.PackMeta; +import com.tacz.guns.resource.convert.PackConverter; +import com.tacz.guns.resource.convert.UnsafeResourceLocation; +import net.minecraft.server.packs.PackType; +import net.minecraftforge.fml.loading.FMLPaths; +import org.apache.commons.io.FileUtils; +import org.jetbrains.annotations.Nullable; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; + +import static com.tacz.guns.resource.PackConvertor.GSON; +import static com.tacz.guns.resource.convert.ConverterUtils.*; + +public enum FolderPackConverter implements PackConverter { + INSTANCE + ; + private final List processors = new ArrayList<>(); + + { + processors.add(createFileRenamer("pack.json", "pack_info.json", PackType.CLIENT_RESOURCES)); + processors.add(createFolderRenamer("tags", "tacz_tags", PackType.SERVER_DATA)); + processors.add(createFolderMover("player_animator", PackType.CLIENT_RESOURCES)); + processors.add(createFolderRenamer("sounds", "tacz_sounds", PackType.CLIENT_RESOURCES)); + processors.add(createFolderMover("textures", PackType.CLIENT_RESOURCES)); + processors.add(createFolderMover("animations", PackType.CLIENT_RESOURCES)); + processors.add(createFolderMover("lang", PackType.CLIENT_RESOURCES)); + processors.add(createFolderRenamer("models", "geo_models", PackType.CLIENT_RESOURCES)); + processors.add(createSubFolderMover("display", PackType.CLIENT_RESOURCES)); + processors.add(createSubFolderMover("index", PackType.SERVER_DATA)); + processors.add(createSubFolderMover("data", PackType.SERVER_DATA)); + processors.add(createCategoryFileVisitor("recipes", + createJsonFileOperator(json -> { + JsonObject object = json.getAsJsonObject(); + if (!object.has("type")) { + object.addProperty("type", "tacz:gun_smith_table_crafting"); + return object; + } + return null; + }).andThen(new FileOp() { + @Override + public boolean run(File baseDir, File file, UnsafeResourceLocation fileResourceLocation) { + return move(baseDir, file, fileResourceLocation, PackType.SERVER_DATA); + } + + @Override + public String toString() { + return "move file to " + PackType.SERVER_DATA.getDirectory(); + } + }))); + } + + // return false if the given file does not represent a legacy pack + // return true even if conversion failed + @Override + public boolean convert(File baseDir) throws IOException { + if (!baseDir.isDirectory()) { + return false; + } + File[] subFiles = Objects.requireNonNullElseGet(baseDir.listFiles(), () -> new File[0]); + if (subFiles.length == 0) { + return false; + } + String namespace = null; + for (File subFile : subFiles) { + if (subFile.isDirectory()) { + File legacyPackFile = new File(subFile, "pack.json"); + if (legacyPackFile.isFile()) { + BufferedReader reader = Files.newBufferedReader(legacyPackFile.toPath(), StandardCharsets.UTF_8); + PackInfo legacyPackInfo = GSON.fromJson(reader, PackInfo.class); + reader.close(); + if (legacyPackInfo != null) { + namespace = relativePath(baseDir, subFile); + break; + } + } + } + } + if (namespace == null) { + return false; + } + Path resourcePacksPath = FMLPaths.GAMEDIR.get().resolve("tacz"); + Path newPath = resourcePacksPath.resolve(baseDir.getName()); + File newPathAsFile = newPath.toFile(); + if (newPathAsFile.isDirectory()) { + throw new FileAlreadyExistsException("New path already exists"); + } + FileUtils.copyDirectory(baseDir, newPathAsFile); + AtomicBoolean failed = new AtomicBoolean(); + Files.walkFileTree(newPath, new SimpleFileVisitor<>() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + GunMod.LOGGER.debug("Visiting old pack file {}", file); + for (FolderEntryVisitor visitor : processors) { + GunMod.LOGGER.debug("Trying to run visitFile from {}", visitor); + if (visitor.visitFile(newPathAsFile, file.toFile())) { + GunMod.LOGGER.debug("File handled, skipping remaining visitors"); + break; // a visitor handled this file, go next + } + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + GunMod.LOGGER.debug("Visiting old pack directory {}", dir); + if (!newPath.equals(dir)) { // always visit the root directory! + for (FolderEntryVisitor visitor : processors) { + GunMod.LOGGER.debug("Trying to run visitDirectory from {}", visitor); + FileVisitResult result = visitor.visitDirectory(newPathAsFile, dir.toFile()); + if (result != null) { + GunMod.LOGGER.debug("Folder handled with result {}", result); + return result; + } + } + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) { + GunMod.LOGGER.error("Error visiting old pack file", exc); + failed.set(true); + return FileVisitResult.TERMINATE; + } + }); + if (!failed.get()) { + Files.walkFileTree(newPath, new EmptyFolderRemover(newPath)); + PackMeta newPackMeta = new PackMeta(namespace, null); + String newPackMetaAsJson = GSON.toJson(newPackMeta); + Path newPackMetaPath = newPath.resolve("gunpack.meta.json"); + Files.writeString(newPackMetaPath, newPackMetaAsJson, StandardCharsets.UTF_8); + } + return true; + } + + private static FolderEntryVisitor createFileRenamer(String oldPath, String newPath, PackType newFolderType) { + return new FolderEntryVisitor() { + @Override + public boolean visitFile(File baseDir, File subFile) { + UnsafeResourceLocation entry = parseEntryName(baseDir, subFile); + if (entry != null) { + String path = entry.getPath(); + if (path.equals(oldPath)) { + String namespace = entry.getNamespace(); + String newRelativePath = toFilePath(namespace, newPath, newFolderType); + File newFile = new File(baseDir, newRelativePath); + newFile.getParentFile().mkdirs(); + return subFile.renameTo(newFile); + } + } + return false; + } + + @Override + public String toString() { + return "file renamer " + oldPath + " -> " + newPath + " (in " + newFolderType.getDirectory() + ")"; + } + }; + } + + private static FolderEntryVisitor createFolderRenamer(String oldPath, String newPath, PackType folderType) { + return new FolderEntryVisitor() { + @Override + public @Nullable FileVisitResult visitDirectory(File baseDir, File subFolder) { + UnsafeResourceLocation entry = parseEntryName(baseDir, subFolder); + if (entry != null) { + String path = entry.getPath(); + if (path.equals(oldPath)) { + String namespace = entry.getNamespace(); + String newRelativePath = toFilePath(namespace, newPath, folderType); + File newFile = new File(baseDir, newRelativePath); + newFile.getParentFile().mkdirs(); + subFolder.renameTo(newFile); + return FileVisitResult.SKIP_SUBTREE; + } + } + return null; + } + + @Override + public String toString() { + return "folder renamer " + oldPath + " -> " + newPath + " (in " + folderType.getDirectory() + ")"; + } + }; + } + + private static FolderEntryVisitor createSubFolderMover(String path, PackType folderType) { + return new FolderEntryVisitor() { + @Override + public @Nullable FileVisitResult visitDirectory(File baseDir, File subFolder) { + UnsafeResourceLocation entry = parseEntryName(baseDir, subFolder); + if (entry != null) { + String entryPath = entry.getPath(); + int i = entryPath.indexOf('/'); + if (i != -1) { + String subPath = entryPath.substring(i + 1); + if (subPath.equals(path)) { + String parentPath = entryPath.substring(0, i); + String namespace = entry.getNamespace(); + String newRelativePath = toFilePath(namespace, path + "/" + parentPath, folderType); + File newFile = new File(baseDir, newRelativePath); + newFile.getParentFile().mkdirs(); + subFolder.renameTo(newFile); + return FileVisitResult.SKIP_SUBTREE; + } + } + } + return null; + } + + @Override + public String toString() { + return "sub folder mover " + path + " to " + folderType.getDirectory(); + } + }; + } + + private static FolderEntryVisitor createFolderMover(String path, PackType folderType) { + return createFolderRenamer(path, path, folderType); + } + + private static FolderEntryVisitor createCategoryFileVisitor(String category, FileOp fileOperator) { + String categoryPlusSlash = category + "/"; + return new FolderEntryVisitor() { + @Override + public boolean visitFile(File baseDir, File subFile) throws IOException { + UnsafeResourceLocation entry = parseEntryName(baseDir, subFile); + if (entry != null) { + String path = entry.getPath(); + if (path.startsWith(categoryPlusSlash)) { + return fileOperator.run(baseDir, subFile, entry); + } + } + return false; + } + + @Override + public String toString() { + return "category file visitor (category " + category + ", file operator " + fileOperator + ")"; + } + }; + } + + private static FileOp createJsonFileOperator(Function jsonOperator) { + return new FileOp() { + @Override + public boolean run(File baseDir, File file, UnsafeResourceLocation fileResourceLocation) throws IOException { + if (file.getName().endsWith(".json")) { + Path asPath = file.toPath(); + BufferedReader reader = Files.newBufferedReader(asPath, StandardCharsets.UTF_8); + JsonElement oldJson = JsonParser.parseReader(reader); + reader.close(); + JsonElement newJson = jsonOperator.apply(oldJson); + if (newJson != null) { + BufferedWriter writer = Files.newBufferedWriter(asPath, StandardCharsets.UTF_8); + GSON.toJson(newJson, writer); + writer.close(); + return true; + } + } + return false; + } + + @Override + public String toString() { + return "json file modifier"; + } + }; + } + + private static boolean move(File baseDir, File file, UnsafeResourceLocation location, PackType folderType) { + String newPath = toFilePath(location, folderType); + File newFile = new File(baseDir, newPath); + newFile.getParentFile().mkdirs(); + return file.renameTo(newFile); + } +} diff --git a/src/main/java/com/tacz/guns/util/ThrowingRunnable.java b/src/main/java/com/tacz/guns/util/ThrowingRunnable.java new file mode 100644 index 000000000..4880a0332 --- /dev/null +++ b/src/main/java/com/tacz/guns/util/ThrowingRunnable.java @@ -0,0 +1,5 @@ +package com.tacz.guns.util; + +public interface ThrowingRunnable { + void run() throws T; +}