Skip to content

Commit f0703f6

Browse files
authored
本地按照实际安装的mod重新打包 (#35)
* fix(gradle): fix non-UTF-8 envrionment cause gradle command break * feat: try get modlist from modloader * feat(core): 本地按模组打包 * refactor: 注释调试日志输出 * feat(core): 添加测速、尝试通过pop out提前结束converter * feat: 优化代码 * feat(convert):提高converter性能 * fix(convert): 删除提前结束功能 * 删去无用Log * fix: 修复特点版本不进行转换的问题 * feat: 支持JarInJar * 删去无用Log * 移动mod domain提取逻辑 * 删去无用日志
1 parent 3f74fc2 commit f0703f6

9 files changed

Lines changed: 133 additions & 33 deletions

File tree

build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ java {
1313
targetCompatibility = JavaVersion.VERSION_1_8
1414
}
1515

16+
tasks.withType<JavaCompile> {
17+
options.encoding = "UTF-8"
18+
}
19+
1620
tasks.shadowJar {
1721
manifest {
1822
attributes(

src/main/java/i18nupdatemod/I18nUpdateMod.java

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@
1010
import i18nupdatemod.entity.GameMetaData;
1111
import i18nupdatemod.util.FileUtil;
1212
import i18nupdatemod.util.Log;
13+
import org.jetbrains.annotations.NotNull;
1314

14-
import java.io.InputStream;
15-
import java.io.InputStreamReader;
15+
import java.io.*;
1616
import java.nio.file.Files;
1717
import java.nio.file.Path;
1818
import java.nio.file.Paths;
1919
import java.util.ArrayList;
20+
import java.util.HashSet;
2021
import java.util.List;
2122
import java.util.Objects;
2223
import java.util.stream.Collectors;
@@ -28,12 +29,15 @@ public class I18nUpdateMod {
2829

2930
public static final Gson GSON = new Gson();
3031

31-
public static void init(Path minecraftPath, String minecraftVersion, String loader) {
32+
public static void init(Path minecraftPath, String minecraftVersion, String loader, @NotNull HashSet<String> modDomainsSet) {
3233
try (InputStream is = I18nUpdateMod.class.getResourceAsStream("/i18nMetaData.json")) {
3334
MOD_VERSION = GSON.fromJson(new InputStreamReader(is), JsonObject.class).get("version").getAsString();
3435
} catch (Exception e) {
3536
Log.warning("Error getting version: " + e);
3637
}
38+
39+
modDomainsSet.remove("i18nupdatemod");
40+
3741
Log.info(String.format("I18nUpdate Mod %s is loaded in %s with %s", MOD_VERSION, minecraftVersion, loader));
3842
Log.debug(String.format("Minecraft path: %s", minecraftPath));
3943
String localStorage = getLocalStoragePos(minecraftPath);
@@ -62,23 +66,19 @@ public static void init(Path minecraftPath, String minecraftVersion, String load
6266

6367
//Update resource pack
6468
List<ResourcePack> languagePacks = new ArrayList<>();
65-
boolean convertNotNeed = assets.downloads.size() == 1 && assets.downloads.get(0).targetVersion.equals(minecraftVersion);
66-
String applyFileName = assets.downloads.get(0).fileName;
6769
for (GameAssetDetail.AssetDownloadDetail it : assets.downloads) {
6870
FileUtil.setTemporaryDirPath(Paths.get(localStorage, "." + MOD_ID, it.targetVersion));
69-
ResourcePack languagePack = new ResourcePack(it.fileName, convertNotNeed);
71+
ResourcePack languagePack = new ResourcePack(it.fileName);
7072
languagePack.checkUpdate(it.fileUrl, it.md5Url);
7173
languagePacks.add(languagePack);
7274
}
7375

7476
//Convert resourcepack
75-
if (!convertNotNeed) {
76-
FileUtil.setTemporaryDirPath(Paths.get(localStorage, "." + MOD_ID, minecraftVersion));
77-
applyFileName = assets.covertFileName;
78-
GameMetaData metaData = I18nConfig.getPackFormat(minecraftVersion);
79-
ResourcePackConverter converter = new ResourcePackConverter(languagePacks, applyFileName);
80-
converter.convert(metaData, getResourcePackDescription(assets.downloads));
81-
}
77+
FileUtil.setTemporaryDirPath(Paths.get(localStorage, "." + MOD_ID, minecraftVersion));
78+
String applyFileName = assets.covertFileName;
79+
GameMetaData metaData = I18nConfig.getPackFormat(minecraftVersion);
80+
ResourcePackConverter converter = new ResourcePackConverter(languagePacks, applyFileName);
81+
converter.convert(metaData, getResourcePackDescription(assets.downloads), modDomainsSet);
8282

8383
//Apply resource pack
8484
GameConfig config = new GameConfig(minecraftPath.resolve("options.txt"));
@@ -123,4 +123,5 @@ public static String getLocalStoragePos(Path minecraftPath) {
123123
Objects::nonNull
124124
).findFirst().orElse(xdgDataHome);
125125
}
126+
126127
}

src/main/java/i18nupdatemod/core/ResourcePack.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,15 @@ public class ResourcePack {
2222
private final String filename;
2323
private final Path filePath;
2424
private final Path tmpFilePath;
25-
private final boolean saveToGame;
2625
private String remoteMd5;
2726

28-
public ResourcePack(String filename, boolean saveToGame) {
27+
public ResourcePack(String filename) {
2928
//If target version is not current version, not save
30-
this.saveToGame = saveToGame;
3129
this.filename = filename;
3230
this.filePath = FileUtil.getResourcePackPath(filename);
3331
this.tmpFilePath = FileUtil.getTemporaryPath(filename);
3432
try {
35-
FileUtil.syncTmpFile(filePath, tmpFilePath, saveToGame);
33+
FileUtil.syncTmpFile(filePath, tmpFilePath);
3634
} catch (Exception e) {
3735
Log.warning(
3836
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 {
8987
if (!Files.exists(tmpFilePath)) {
9088
throw new FileNotFoundException("Tmp file not found.");
9189
}
92-
FileUtil.syncTmpFile(filePath, tmpFilePath, saveToGame);
90+
FileUtil.syncTmpFile(filePath, tmpFilePath);
9391
}
9492

9593
public Path getTmpFilePath() {

src/main/java/i18nupdatemod/core/ResourcePackConverter.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,25 +33,28 @@ public ResourcePackConverter(List<ResourcePack> resourcePack, String filename) {
3333
this.tmpFilePath = FileUtil.getTemporaryPath(filename);
3434
}
3535

36-
public void convert(GameMetaData metaData, String description) throws Exception {
36+
public void convert(GameMetaData metaData, String description, HashSet<String> modDomainsSet) throws Exception {
3737
Set<String> fileList = new HashSet<>();
38-
try (ZipOutputStream zos = new ZipOutputStream(
39-
Files.newOutputStream(tmpFilePath),
40-
StandardCharsets.UTF_8)) {
38+
try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(tmpFilePath), StandardCharsets.UTF_8)) {
4139
// zos.setMethod(ZipOutputStream.STORED);
4240
for (Path p : sourcePath) {
4341
Log.info("Converting: " + p);
4442
try (ZipFile zf = new ZipFile(p.toFile(), StandardCharsets.UTF_8)) {
4543
for (Enumeration<? extends ZipEntry> e = zf.entries(); e.hasMoreElements(); ) {
4644
ZipEntry ze = e.nextElement();
4745
String name = ze.getName();
46+
String[] parts = name.split("/");
47+
// 正在筛选的是assets/modDomain/** && 当前的modDomain不需要
48+
if (parts.length >= 2 && !modDomainsSet.contains(parts[1])) {
49+
continue;
50+
}
51+
4852
// Don't put same file
4953
if (fileList.contains(name)) {
5054
// Log.debug(name + ": DUPLICATE");
5155
continue;
5256
}
5357
fileList.add(name);
54-
// Log.debug(name);
5558

5659
// Put file into new zip
5760
zos.putNextEntry(new ZipEntry(name));
@@ -69,7 +72,7 @@ public void convert(GameMetaData metaData, String description) throws Exception
6972
}
7073
zos.close();
7174
Log.info("Converted: %s -> %s", sourcePath, tmpFilePath);
72-
FileUtil.syncTmpFile(tmpFilePath, filePath, true);
75+
FileUtil.syncTmpFile(tmpFilePath, filePath);
7376
} catch (Exception e) {
7477
throw new Exception(String.format("Error converting %s to %s: %s", sourcePath, tmpFilePath, e));
7578
}

src/main/java/i18nupdatemod/fabricloader/FabricLoaderMod.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import net.fabricmc.loader.api.FabricLoader;
88

99
import java.nio.file.Path;
10+
import java.util.HashSet;
11+
import java.util.Map;
1012

1113
//1.14-latest
1214
public class FabricLoaderMod implements ClientModInitializer {
@@ -20,7 +22,7 @@ public void onInitializeClient() {
2022
Log.warning("Minecraft version not found");
2123
return;
2224
}
23-
I18nUpdateMod.init(gameDir, mcVersion, "Fabric");
25+
I18nUpdateMod.init(gameDir, mcVersion, "Fabric", getMods());
2426
}
2527

2628
private String getMcVersion() {
@@ -44,4 +46,30 @@ private String getMcVersion() {
4446
}
4547
return null;
4648
}
49+
50+
51+
private HashSet<String> getMods() {
52+
HashSet<String> modList = new HashSet<>();
53+
try {
54+
// Fabric
55+
@SuppressWarnings("unchecked") final Map<String, Object> instance = (Map<String, Object>) Reflection.clazz("net.fabricmc.loader.impl.FabricLoaderImpl")
56+
.get("INSTANCE")
57+
.get("modMap").get();
58+
modList = new HashSet<>(instance.keySet());
59+
return modList;
60+
} catch (Exception ignored) {
61+
62+
}
63+
try {
64+
// Quilt
65+
@SuppressWarnings("unchecked") final Map<String, Object> instance = (Map<String, Object>) Reflection.clazz("org.quiltmc.loader.impl.QuiltLoaderImpl")
66+
.get("INSTANCE")
67+
.get("modMap").get();
68+
modList = new HashSet<>(instance.keySet());
69+
return modList;
70+
} catch (Exception ignored) {
71+
72+
}
73+
return modList;
74+
}
4775
}

src/main/java/i18nupdatemod/launchwrapper/LaunchWrapperTweaker.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import i18nupdatemod.I18nUpdateMod;
44
import i18nupdatemod.util.Log;
5+
import i18nupdatemod.util.ModUtil;
56
import i18nupdatemod.util.Reflection;
67
import net.minecraft.launchwrapper.ITweaker;
78
import net.minecraft.launchwrapper.LaunchClassLoader;
@@ -20,7 +21,7 @@ public void acceptOptions(List<String> args, File gameDir, File assetsDir, Strin
2021
Log.warning("Failed to get minecraft version.");
2122
return;
2223
}
23-
I18nUpdateMod.init(gameDir.toPath(), mcVersion, "Forge");
24+
I18nUpdateMod.init(gameDir.toPath(), mcVersion, "Forge", ModUtil.getModDomainsFromModsFolder(gameDir.toPath(), mcVersion, "Forge"));
2425
}
2526

2627
@Override

src/main/java/i18nupdatemod/modlauncher/ModLauncherService.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,14 @@
88
import cpw.mods.modlauncher.api.IncompatibleEnvironmentException;
99
import i18nupdatemod.I18nUpdateMod;
1010
import i18nupdatemod.util.Log;
11+
import i18nupdatemod.util.ModUtil;
1112
import i18nupdatemod.util.Reflection;
1213
import org.jetbrains.annotations.NotNull;
1314

1415
import java.io.InputStream;
1516
import java.io.InputStreamReader;
1617
import java.nio.file.Path;
17-
import java.util.Collections;
18-
import java.util.List;
19-
import java.util.Optional;
20-
import java.util.Set;
18+
import java.util.*;
2119

2220
import static i18nupdatemod.I18nUpdateMod.GSON;
2321

@@ -41,7 +39,7 @@ public void initialize(IEnvironment environment) {
4139
Log.warning("Minecraft version not found");
4240
return;
4341
}
44-
I18nUpdateMod.init(minecraftPath.get(), minecraftVersion, "Forge");
42+
I18nUpdateMod.init(minecraftPath.get(), minecraftVersion, "Forge", ModUtil.getModDomainsFromModsFolder(minecraftPath.get(), minecraftVersion, "Forge"));
4543
}
4644

4745
@Override

src/main/java/i18nupdatemod/util/FileUtil.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public static Path getTemporaryPath(String filename) {
3737
return temporaryDirPath.resolve(filename);
3838
}
3939

40-
public static void syncTmpFile(Path filePath, Path tmpFilePath, boolean saveToGame) throws IOException {
40+
public static void syncTmpFile(Path filePath, Path tmpFilePath) throws IOException {
4141
//Both temp and current file not found
4242
if (!Files.exists(filePath) && !Files.exists(tmpFilePath)) {
4343
Log.debug("Both temp and current file not found");
@@ -59,7 +59,7 @@ public static void syncTmpFile(Path filePath, Path tmpFilePath, boolean saveToGa
5959
to = filePath;
6060
}
6161

62-
if (!saveToGame && to == filePath) {
62+
if (to == filePath) {
6363
//Don't save to game
6464
return;
6565
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package i18nupdatemod.util;
2+
3+
import java.io.*;
4+
import java.nio.file.Path;
5+
import java.util.HashSet;
6+
import java.util.jar.JarEntry;
7+
import java.util.jar.JarInputStream;
8+
9+
public class ModUtil {
10+
public static HashSet<String> getModDomainsFromModsFolder(Path minecraftPath, String minecraftVersion, String loader) {
11+
HashSet<String> modDomainSet = new HashSet<>();
12+
Path modsPath = minecraftPath.resolve("mods");
13+
String[] modsNamesList = modsPath.toFile().list((dir, name) -> name.endsWith(".jar"));
14+
if (modsNamesList != null) {
15+
for (String name : modsNamesList) {
16+
modDomainSet.addAll(getModDomainFromJar(modsPath.resolve(name).toFile()));
17+
}
18+
}
19+
return modDomainSet;
20+
}
21+
22+
private static HashSet<String> getModDomainFromJar(File modPath) {
23+
Log.debug(String.format("Get mod domain from %s", modPath));
24+
HashSet<String> modList = new HashSet<>();
25+
try (FileInputStream fis = new FileInputStream(modPath)) {
26+
modList.addAll(getModDomainFromStream(fis, modPath.getName()));
27+
} catch (Exception e) {
28+
Log.warning(String.format("Failed to read jar %s: %s", modPath, e));
29+
}
30+
return modList;
31+
}
32+
33+
private static HashSet<String> getModDomainFromStream(InputStream input, String sourceName) throws IOException {
34+
HashSet<String> modList = new HashSet<>();
35+
try (JarInputStream jis = new JarInputStream(input)) {
36+
JarEntry entry;
37+
byte[] buffer = new byte[8192];
38+
while ((entry = jis.getNextJarEntry()) != null) {
39+
String path = entry.getName();
40+
41+
// 匹配 assets/<domain>/
42+
if (path.startsWith("assets/")) {
43+
String[] parts = path.split("/");
44+
if (parts.length >= 2) {
45+
modList.add(parts[1]);
46+
}
47+
} else if (path.endsWith(".jar")) {
48+
try {
49+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
50+
int bytesRead;
51+
while ((bytesRead = jis.read(buffer)) != -1) {
52+
baos.write(buffer, 0, bytesRead);
53+
}
54+
55+
byte[] innerJarBytes = baos.toByteArray();
56+
try (ByteArrayInputStream innerStream = new ByteArrayInputStream(innerJarBytes)) {
57+
modList.addAll(getModDomainFromStream(innerStream, path));
58+
}
59+
} catch (Exception innerEx) {
60+
Log.warning(String.format("Failed to parse nested jar %s inside %s: %s", path, sourceName, innerEx));
61+
}
62+
}
63+
}
64+
}
65+
return modList;
66+
}
67+
}

0 commit comments

Comments
 (0)