diff --git a/build.gradle.kts b/build.gradle.kts index c4288ad..12fcfd9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,6 +13,10 @@ java { targetCompatibility = JavaVersion.VERSION_1_8 } +tasks.withType { + options.encoding = "UTF-8" +} + tasks.shadowJar { manifest { attributes( diff --git a/src/main/java/i18nupdatemod/I18nUpdateMod.java b/src/main/java/i18nupdatemod/I18nUpdateMod.java index 1afd352..b891c9e 100644 --- a/src/main/java/i18nupdatemod/I18nUpdateMod.java +++ b/src/main/java/i18nupdatemod/I18nUpdateMod.java @@ -9,13 +9,14 @@ import i18nupdatemod.entity.GameAssetDetail; import i18nupdatemod.util.FileUtil; import i18nupdatemod.util.Log; +import org.jetbrains.annotations.NotNull; -import java.io.InputStream; -import java.io.InputStreamReader; +import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; @@ -27,12 +28,16 @@ public class I18nUpdateMod { public static final Gson GSON = new Gson(); - public static void init(Path minecraftPath, String minecraftVersion, String loader) { + public static void init(Path minecraftPath, String minecraftVersion, String loader, @NotNull HashSet modDomainsSet) { + long startTime = System.nanoTime(); try (InputStream is = I18nUpdateMod.class.getResourceAsStream("/i18nMetaData.json")) { MOD_VERSION = GSON.fromJson(new InputStreamReader(is), JsonObject.class).get("version").getAsString(); } catch (Exception e) { Log.warning("Error getting version: " + e); } + + modDomainsSet.remove("i18nupdatemod"); + Log.info(String.format("I18nUpdate Mod %s is loaded in %s with %s", MOD_VERSION, minecraftVersion, loader)); Log.debug(String.format("Minecraft path: %s", minecraftPath)); String localStorage = getLocalStoragePos(minecraftPath); @@ -61,28 +66,26 @@ public static void init(Path minecraftPath, String minecraftVersion, String load //Update resource pack List languagePacks = new ArrayList<>(); - boolean convertNotNeed = assets.downloads.size() == 1 && assets.downloads.get(0).targetVersion.equals(minecraftVersion); - String applyFileName = assets.downloads.get(0).fileName; for (GameAssetDetail.AssetDownloadDetail it : assets.downloads) { FileUtil.setTemporaryDirPath(Paths.get(localStorage, "." + MOD_ID, it.targetVersion)); - ResourcePack languagePack = new ResourcePack(it.fileName, convertNotNeed); + ResourcePack languagePack = new ResourcePack(it.fileName); languagePack.checkUpdate(it.fileUrl, it.md5Url); languagePacks.add(languagePack); } //Convert resourcepack - if (!convertNotNeed) { - FileUtil.setTemporaryDirPath(Paths.get(localStorage, "." + MOD_ID, minecraftVersion)); - applyFileName = assets.covertFileName; - ResourcePackConverter converter = new ResourcePackConverter(languagePacks, applyFileName); - converter.convert(assets.covertPackFormat, getResourcePackDescription(assets.downloads)); - } + FileUtil.setTemporaryDirPath(Paths.get(localStorage, "." + MOD_ID, minecraftVersion)); + String applyFileName = assets.covertFileName; + ResourcePackConverter converter = new ResourcePackConverter(languagePacks, applyFileName); + converter.convert(assets.covertPackFormat, getResourcePackDescription(assets.downloads), modDomainsSet); //Apply resource pack GameConfig config = new GameConfig(minecraftPath.resolve("options.txt")); config.addResourcePack("Minecraft-Mod-Language-Modpack", (minecraftMajorVersion <= 12 ? "" : "file/") + applyFileName); config.writeToFile(); + long endTime = System.nanoTime(); + Log.info(String.format("I18nUpdateMod finished in %.2f ms", (endTime - startTime) / 1000000.0)); } catch (Exception e) { Log.warning(String.format("Failed to update resource pack: %s", e)); // e.printStackTrace(); @@ -121,4 +124,5 @@ public static String getLocalStoragePos(Path minecraftPath) { Objects::nonNull ).findFirst().orElse(xdgDataHome); } + } \ No newline at end of file diff --git a/src/main/java/i18nupdatemod/core/ResourcePack.java b/src/main/java/i18nupdatemod/core/ResourcePack.java index 6c890e5..91e1700 100644 --- a/src/main/java/i18nupdatemod/core/ResourcePack.java +++ b/src/main/java/i18nupdatemod/core/ResourcePack.java @@ -22,17 +22,15 @@ public class ResourcePack { private final String filename; private final Path filePath; private final Path tmpFilePath; - private final boolean saveToGame; private String remoteMd5; - public ResourcePack(String filename, boolean saveToGame) { + public ResourcePack(String filename) { //If target version is not current version, not save - this.saveToGame = saveToGame; this.filename = filename; this.filePath = FileUtil.getResourcePackPath(filename); this.tmpFilePath = FileUtil.getTemporaryPath(filename); try { - FileUtil.syncTmpFile(filePath, tmpFilePath, saveToGame); + FileUtil.syncTmpFile(filePath, tmpFilePath); } catch (Exception e) { Log.warning( String.format("Error while sync temp file %s <-> %s: %s", filePath, tmpFilePath, e)); @@ -89,7 +87,7 @@ private void downloadFull(String fileUrl, String md5Url) throws IOException { if (!Files.exists(tmpFilePath)) { throw new FileNotFoundException("Tmp file not found."); } - FileUtil.syncTmpFile(filePath, tmpFilePath, saveToGame); + FileUtil.syncTmpFile(filePath, tmpFilePath); } public Path getTmpFilePath() { diff --git a/src/main/java/i18nupdatemod/core/ResourcePackConverter.java b/src/main/java/i18nupdatemod/core/ResourcePackConverter.java index 0a97712..d1dd051 100644 --- a/src/main/java/i18nupdatemod/core/ResourcePackConverter.java +++ b/src/main/java/i18nupdatemod/core/ResourcePackConverter.java @@ -32,11 +32,9 @@ public ResourcePackConverter(List resourcePack, String filename) { this.tmpFilePath = FileUtil.getTemporaryPath(filename); } - public void convert(int packFormat, String description) throws Exception { + public void convert(int packFormat, String description, HashSet modDomainsSet) throws Exception { Set fileList = new HashSet<>(); - try (ZipOutputStream zos = new ZipOutputStream( - Files.newOutputStream(tmpFilePath), - StandardCharsets.UTF_8)) { + try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(tmpFilePath), StandardCharsets.UTF_8)) { // zos.setMethod(ZipOutputStream.STORED); for (Path p : sourcePath) { Log.info("Converting: " + p); @@ -44,13 +42,18 @@ public void convert(int packFormat, String description) throws Exception { for (Enumeration e = zf.entries(); e.hasMoreElements(); ) { ZipEntry ze = e.nextElement(); String name = ze.getName(); + String[] parts = name.split("/"); + // 正在筛选的是assets/modDomain/** && 当前的modDomain不需要 + if (parts.length >= 2 && !modDomainsSet.contains(parts[1])) { + continue; + } + // Don't put same file if (fileList.contains(name)) { // Log.debug(name + ": DUPLICATE"); continue; } fileList.add(name); -// Log.debug(name); // Put file into new zip zos.putNextEntry(new ZipEntry(name)); @@ -67,8 +70,9 @@ public void convert(int packFormat, String description) throws Exception { } } zos.close(); + //Log.debug("unsolved mod domains" + modDomainsSet.toString()); Log.info("Converted: %s -> %s", sourcePath, tmpFilePath); - FileUtil.syncTmpFile(tmpFilePath, filePath, true); + FileUtil.syncTmpFile(tmpFilePath, filePath); } catch (Exception e) { throw new Exception(String.format("Error converting %s to %s: %s", sourcePath, tmpFilePath, e)); } diff --git a/src/main/java/i18nupdatemod/fabricloader/FabricLoaderMod.java b/src/main/java/i18nupdatemod/fabricloader/FabricLoaderMod.java index ad740a0..e2bf81c 100644 --- a/src/main/java/i18nupdatemod/fabricloader/FabricLoaderMod.java +++ b/src/main/java/i18nupdatemod/fabricloader/FabricLoaderMod.java @@ -7,6 +7,8 @@ import net.fabricmc.loader.api.FabricLoader; import java.nio.file.Path; +import java.util.HashSet; +import java.util.Map; //1.14-latest public class FabricLoaderMod implements ClientModInitializer { @@ -20,7 +22,7 @@ public void onInitializeClient() { Log.warning("Minecraft version not found"); return; } - I18nUpdateMod.init(gameDir, mcVersion, "Fabric"); + I18nUpdateMod.init(gameDir, mcVersion, "Fabric", getMods()); } private String getMcVersion() { @@ -44,4 +46,30 @@ private String getMcVersion() { } return null; } + + + private HashSet getMods() { + HashSet modList = new HashSet<>(); + try { + // Fabric + @SuppressWarnings("unchecked") final Map instance = (Map) Reflection.clazz("net.fabricmc.loader.impl.FabricLoaderImpl") + .get("INSTANCE") + .get("modMap").get(); + modList = new HashSet<>(instance.keySet()); + return modList; + } catch (Exception ignored) { + + } + try { + // Quilt + @SuppressWarnings("unchecked") final Map instance = (Map) Reflection.clazz("org.quiltmc.loader.impl.QuiltLoaderImpl") + .get("INSTANCE") + .get("modMap").get(); + modList = new HashSet<>(instance.keySet()); + return modList; + } catch (Exception ignored) { + + } + return modList; + } } diff --git a/src/main/java/i18nupdatemod/launchwrapper/LaunchWrapperTweaker.java b/src/main/java/i18nupdatemod/launchwrapper/LaunchWrapperTweaker.java index 72e522f..dab5b80 100644 --- a/src/main/java/i18nupdatemod/launchwrapper/LaunchWrapperTweaker.java +++ b/src/main/java/i18nupdatemod/launchwrapper/LaunchWrapperTweaker.java @@ -2,6 +2,7 @@ import i18nupdatemod.I18nUpdateMod; import i18nupdatemod.util.Log; +import i18nupdatemod.util.ModUtil; import i18nupdatemod.util.Reflection; import net.minecraft.launchwrapper.ITweaker; import net.minecraft.launchwrapper.LaunchClassLoader; @@ -20,7 +21,7 @@ public void acceptOptions(List args, File gameDir, File assetsDir, Strin Log.warning("Failed to get minecraft version."); return; } - I18nUpdateMod.init(gameDir.toPath(), mcVersion, "Forge"); + I18nUpdateMod.init(gameDir.toPath(), mcVersion, "Forge", ModUtil.getModDomainsFromModsFolder(gameDir.toPath(), mcVersion, "Forge")); } @Override diff --git a/src/main/java/i18nupdatemod/modlauncher/ModLauncherService.java b/src/main/java/i18nupdatemod/modlauncher/ModLauncherService.java index f8323e4..33e0421 100644 --- a/src/main/java/i18nupdatemod/modlauncher/ModLauncherService.java +++ b/src/main/java/i18nupdatemod/modlauncher/ModLauncherService.java @@ -8,16 +8,14 @@ import cpw.mods.modlauncher.api.IncompatibleEnvironmentException; import i18nupdatemod.I18nUpdateMod; import i18nupdatemod.util.Log; +import i18nupdatemod.util.ModUtil; import i18nupdatemod.util.Reflection; import org.jetbrains.annotations.NotNull; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.file.Path; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.Set; +import java.util.*; import static i18nupdatemod.I18nUpdateMod.GSON; @@ -41,7 +39,7 @@ public void initialize(IEnvironment environment) { Log.warning("Minecraft version not found"); return; } - I18nUpdateMod.init(minecraftPath.get(), minecraftVersion, "Forge"); + I18nUpdateMod.init(minecraftPath.get(), minecraftVersion, "Forge", ModUtil.getModDomainsFromModsFolder(minecraftPath.get(), minecraftVersion, "Forge")); } @Override diff --git a/src/main/java/i18nupdatemod/util/FileUtil.java b/src/main/java/i18nupdatemod/util/FileUtil.java index 6cc3410..8c5f818 100644 --- a/src/main/java/i18nupdatemod/util/FileUtil.java +++ b/src/main/java/i18nupdatemod/util/FileUtil.java @@ -37,7 +37,7 @@ public static Path getTemporaryPath(String filename) { return temporaryDirPath.resolve(filename); } - public static void syncTmpFile(Path filePath, Path tmpFilePath, boolean saveToGame) throws IOException { + public static void syncTmpFile(Path filePath, Path tmpFilePath) throws IOException { //Both temp and current file not found if (!Files.exists(filePath) && !Files.exists(tmpFilePath)) { Log.debug("Both temp and current file not found"); @@ -59,7 +59,7 @@ public static void syncTmpFile(Path filePath, Path tmpFilePath, boolean saveToGa to = filePath; } - if (!saveToGame && to == filePath) { + if (to == filePath) { //Don't save to game return; } diff --git a/src/main/java/i18nupdatemod/util/ModUtil.java b/src/main/java/i18nupdatemod/util/ModUtil.java new file mode 100644 index 0000000..b798c82 --- /dev/null +++ b/src/main/java/i18nupdatemod/util/ModUtil.java @@ -0,0 +1,67 @@ +package i18nupdatemod.util; + +import java.io.*; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; + +public class ModUtil { + public static HashSet getModDomainsFromModsFolder(Path minecraftPath, String minecraftVersion, String loader) { + HashSet modDomainSet = new HashSet<>(); + Path modsPath = minecraftPath.resolve("mods"); + String[] modsNamesList = modsPath.toFile().list((dir, name) -> name.endsWith(".jar")); + if (modsNamesList != null) { + for (String name : modsNamesList) { + modDomainSet.addAll(getModDomainFromJar(modsPath.resolve(name).toFile())); + } + } + return modDomainSet; + } + + private static HashSet getModDomainFromJar(File modPath) { + Log.debug(String.format("Get mod domain from %s", modPath)); + HashSet modList = new HashSet<>(); + try (FileInputStream fis = new FileInputStream(modPath)) { + modList.addAll(getModDomainFromStream(fis, modPath.getName())); + } catch (Exception e) { + Log.warning(String.format("Failed to read jar %s: %s", modPath, e)); + } + return modList; + } + + private static HashSet getModDomainFromStream(InputStream input, String sourceName) throws IOException { + HashSet modList = new HashSet<>(); + try (JarInputStream jis = new JarInputStream(input)) { + JarEntry entry; + byte[] buffer = new byte[8192]; + while ((entry = jis.getNextJarEntry()) != null) { + String path = entry.getName(); + + // 匹配 assets// + if (path.startsWith("assets/")) { + String[] parts = path.split("/"); + if (parts.length >= 2) { + modList.add(parts[1]); + } + } else if (path.endsWith(".jar")) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int bytesRead; + while ((bytesRead = jis.read(buffer)) != -1) { + baos.write(buffer, 0, bytesRead); + } + + byte[] innerJarBytes = baos.toByteArray(); + try (ByteArrayInputStream innerStream = new ByteArrayInputStream(innerJarBytes)) { + modList.addAll(getModDomainFromStream(innerStream, path)); + } + } catch (Exception innerEx) { + Log.warning(String.format("Failed to parse nested jar %s inside %s: %s", path, sourceName, innerEx)); + } + } + } + } + return modList; + } +}