Skip to content

Commit f423d11

Browse files
authored
Fix ResourceLanguageProvider not loading language files correctly (#152)
* Fix failure to load language files from file resourcepacks if your resource pack version is too low (MC 1.14.4-1.19.3) * Fix reloading resource packs without reloading languages * Fix failure to load language files from zip resourcepacks --------- Signed-off-by: Hendrix-Shen <[email protected]>
1 parent 2f1ef25 commit f423d11

File tree

11 files changed

+206
-50
lines changed

11 files changed

+206
-50
lines changed

magiclib-core/common/src/main/java/top/hendrixshen/magiclib/api/i18n/LanguageProvider.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package top.hendrixshen.magiclib.api.i18n;
22

33
import java.util.Map;
4+
import java.util.regex.Pattern;
45

56
public interface LanguageProvider {
7+
Pattern LANGUAGE_PATH_PATTERN = Pattern.compile("^assets/([\\w-]*)/lang/([a-zA-Z\\d-_]*)\\.json$");
8+
69
void init();
710

811
void reload();

magiclib-core/common/src/main/java/top/hendrixshen/magiclib/impl/i18n/provider/FileLanguageProvider.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,13 @@
2929
import java.util.List;
3030
import java.util.Map;
3131
import java.util.regex.Matcher;
32-
import java.util.regex.Pattern;
3332

3433
@NoArgsConstructor(access = AccessLevel.PRIVATE)
3534
public class FileLanguageProvider implements LanguageProvider {
3635
@Getter(lazy = true)
3736
private static final FileLanguageProvider instance = new FileLanguageProvider();
3837

3938
private final Map<String, List<Path>> files = Maps.newConcurrentMap();
40-
private final Pattern languageResourcePattern = Pattern.compile("^assets/([\\w-]*)/lang/([a-zA-Z\\d-_]*)\\.json$");
4139

4240
@Override
4341
public void init() {
@@ -100,32 +98,31 @@ public static class LanguageFileVisitor implements FileVisitor<Path> {
10098
private final boolean prefix;
10199

102100
@Override
103-
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
101+
public @NotNull FileVisitResult preVisitDirectory(Path dir, @NotNull BasicFileAttributes attrs) {
104102
return FileVisitResult.CONTINUE;
105103
}
106104

107105
@Override
108-
public FileVisitResult visitFile(@NotNull Path file, BasicFileAttributes attrs) {
106+
public @NotNull FileVisitResult visitFile(@NotNull Path file, @NotNull BasicFileAttributes attrs) {
109107
String name = (prefix ? "" : "assets/") + this.basePath.relativize(file).toString()
110108
.replace("\\", "/");
111-
Matcher matcher = FileLanguageProvider.getInstance().languageResourcePattern.matcher(name);
109+
Matcher matcher = LanguageProvider.LANGUAGE_PATH_PATTERN.matcher(name);
112110

113111
if (!matcher.find()) {
114112
return FileVisitResult.CONTINUE;
115113
}
116114

117-
this.files.computeIfAbsent(matcher.group(2), key ->
118-
Lists.newArrayList()).add(file);
115+
this.files.computeIfAbsent(matcher.group(2), key -> Lists.newArrayList()).add(file);
119116
return FileVisitResult.CONTINUE;
120117
}
121118

122119
@Override
123-
public FileVisitResult visitFileFailed(Path file, IOException exc) {
120+
public @NotNull FileVisitResult visitFileFailed(Path file, @NotNull IOException exc) {
124121
return FileVisitResult.CONTINUE;
125122
}
126123

127124
@Override
128-
public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
125+
public @NotNull FileVisitResult postVisitDirectory(Path dir, IOException exc) {
129126
return FileVisitResult.CONTINUE;
130127
}
131128
}

magiclib-core/common/src/main/java/top/hendrixshen/magiclib/impl/i18n/provider/JarLanguageProvider.java

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package top.hendrixshen.magiclib.impl.i18n.provider;
22

33
import com.google.common.collect.Maps;
4-
import com.google.gson.JsonSyntaxException;
5-
import com.google.gson.stream.MalformedJsonException;
64
import lombok.AccessLevel;
75
import lombok.Getter;
86
import lombok.NoArgsConstructor;
7+
import org.jetbrains.annotations.ApiStatus.Internal;
98
import org.jetbrains.annotations.NotNull;
109

1110
import top.hendrixshen.magiclib.MagicLib;
@@ -18,18 +17,17 @@
1817
import java.net.URL;
1918
import java.util.Collections;
2019
import java.util.Map;
21-
import java.util.jar.JarEntry;
20+
import java.util.function.Function;
2221
import java.util.jar.JarFile;
2322
import java.util.regex.Matcher;
24-
import java.util.regex.Pattern;
23+
import java.util.zip.ZipEntry;
2524

2625
@NoArgsConstructor(access = AccessLevel.PRIVATE)
2726
public class JarLanguageProvider implements LanguageProvider {
2827
@Getter(lazy = true)
2928
private static final JarLanguageProvider instance = new JarLanguageProvider();
3029

3130
private final Map<String, Map<String, String>> languageMap = Maps.newConcurrentMap();
32-
private final Pattern languageResourcePattern = Pattern.compile("^assets/([\\w-]*)/lang/([a-zA-Z\\d-_]*)\\.json$");
3331

3432
@Override
3533
public void init() {
@@ -68,28 +66,34 @@ public Map<String, String> getLanguage(@NotNull String languageCode) {
6866
}
6967

7068
private void loadFromJar(@NotNull JarFile jar) {
71-
for (JarEntry entry : Collections.list(jar.entries())) {
72-
Matcher matcher = languageResourcePattern.matcher(entry.getName());
73-
74-
if (!matcher.find()) {
75-
continue;
76-
}
77-
78-
Map<String, String> language = this.getLanguage(matcher.group(2));
79-
69+
for (ZipEntry entry : Collections.list(jar.entries())) {
8070
try (InputStream inputStream = jar.getInputStream(entry)) {
81-
JsonUtil.loadStringMapFromJson(inputStream, language::put);
82-
MagicLib.getLogger().debug("Loaded language file {} from {}.", entry.getName(), jar.getName());
83-
} catch (Exception e) {
84-
if (e instanceof JsonSyntaxException && e.getCause() instanceof MalformedJsonException) {
85-
MagicLib.getLogger().error("Failed to load language file {} from {}.",
86-
entry.getName(), jar.getName());
87-
continue;
71+
if (JarLanguageProvider.loadFromEntry(entry, inputStream, this::getLanguage)) {
72+
MagicLib.getLogger().debug("Loaded language file {} from {}.", entry.getName(), jar.getName());
8873
}
89-
74+
} catch (IOException e) {
9075
MagicLib.getLogger().error("Failed to load language file {} from {}.",
9176
entry.getName(), jar.getName(), e);
9277
}
9378
}
9479
}
80+
81+
@Internal
82+
public static boolean loadFromEntry(@NotNull ZipEntry entry, InputStream inputStream,
83+
Function<String, Map<String, String>> languageMapGetter) {
84+
Matcher matcher = LanguageProvider.LANGUAGE_PATH_PATTERN.matcher(entry.getName());
85+
86+
if (!matcher.find()) {
87+
return false;
88+
}
89+
90+
Map<String, String> language = languageMapGetter.apply(matcher.group(2));
91+
92+
try {
93+
JsonUtil.loadStringMapFromJson(inputStream, language::put);
94+
return true;
95+
} catch (Exception e) {
96+
return false;
97+
}
98+
}
9599
}

magiclib-minecraft-api/src/main/java/top/hendrixshen/magiclib/impl/i18n/minecraft/MinecraftLanguageManager.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ public static void init() {
2828

2929
@Override
3030
public void postLanguageReload() {
31-
MagicLanguageManager.getInstance().setCurrentCode(Minecraft.getInstance().options.languageCode);
31+
if (!MagicLanguageManager.getInstance().setCurrentCode(Minecraft.getInstance().options.languageCode)) {
32+
MagicLanguageManager.getInstance().reload();
33+
}
3234
}
3335

3436
@Override

magiclib-minecraft-api/src/main/java/top/hendrixshen/magiclib/impl/i18n/minecraft/ResourceLanguageProvider.java

Lines changed: 113 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,42 +6,68 @@
66
import lombok.NoArgsConstructor;
77

88
import net.minecraft.client.Minecraft;
9+
import net.minecraft.server.packs.FilePackResources;
10+
import net.minecraft.server.packs.PackResources;
911
import net.minecraft.server.packs.repository.Pack;
1012

13+
// CHECKSTYLE.OFF: ImportOrder
14+
//#if MC > 11902
15+
//$$ import net.minecraft.server.packs.PathPackResources;
16+
//#else
17+
import net.minecraft.server.packs.FolderPackResources;
18+
//#endif
19+
// CHECKSTYLE.ON: ImportOrder
20+
1121
import top.hendrixshen.magiclib.MagicLib;
1222
import top.hendrixshen.magiclib.api.fake.i18n.PackAccessor;
1323
import top.hendrixshen.magiclib.api.i18n.LanguageProvider;
14-
import top.hendrixshen.magiclib.impl.i18n.provider.FileLanguageProvider;
24+
import top.hendrixshen.magiclib.impl.i18n.provider.FileLanguageProvider.LanguageFileVisitor;
25+
import top.hendrixshen.magiclib.impl.i18n.provider.JarLanguageProvider;
1526
import top.hendrixshen.magiclib.util.JsonUtil;
1627

28+
// CHECKSTYLE.OFF: ImportOrder
29+
//#if 11903 > MC && MC > 11404
30+
import top.hendrixshen.magiclib.mixin.minecraft.accessor.PackResourcesAdapterV4Accessor;
31+
//#endif
32+
33+
//#if MC < 11903
34+
import top.hendrixshen.magiclib.mixin.minecraft.accessor.LegacyPackResourcesAdapterAccessor;
35+
//#endif
36+
// CHECKSTYLE.ON: ImportOrder
37+
38+
import java.io.File;
1739
import java.io.IOException;
1840
import java.io.InputStream;
1941
import java.nio.file.Files;
2042
import java.nio.file.Path;
2143
import java.util.Collections;
2244
import java.util.List;
2345
import java.util.Map;
46+
import java.util.Map.Entry;
47+
import java.util.Objects;
48+
import java.util.zip.ZipEntry;
49+
import java.util.zip.ZipFile;
2450

2551
@NoArgsConstructor(access = AccessLevel.PRIVATE)
2652
public class ResourceLanguageProvider implements LanguageProvider {
2753
@Getter(lazy = true)
2854
private static final ResourceLanguageProvider instance = new ResourceLanguageProvider();
2955

30-
private final Map<String, List<Path>> files = Maps.newConcurrentMap();
56+
private final Map<String, Map<String, String>> languageMap = Maps.newConcurrentMap();
3157

3258
@Override
3359
public void init() {
60+
this.languageMap.clear();
3461
Minecraft.getInstance().getResourcePackRepository().getSelectedPacks().stream()
3562
.filter(pack -> pack.getId().startsWith("file"))
3663
.map(Pack::open)
37-
.filter(PackAccessor.class::isInstance)
38-
.map(pack -> ((PackAccessor) pack).magiclib$getFile().toPath())
39-
.forEach(this::updateFileList);
64+
.map(this::adaptPack)
65+
.filter(Objects::nonNull)
66+
.forEach(this::initLanguageMap);
4067
}
4168

4269
@Override
4370
public void reload() {
44-
this.files.clear();
4571
this.init();
4672
}
4773

@@ -57,25 +83,94 @@ public void loadLanguage(String languageCode) {
5783

5884
@Override
5985
public Map<String, String> getLanguage(String languageCode) {
60-
Map<String, String> result = Maps.newConcurrentMap();
86+
return this.languageMap.getOrDefault(languageCode, Collections.emptyMap());
87+
}
6188

62-
this.files.getOrDefault(languageCode, Collections.emptyList()).forEach(file -> {
63-
try (InputStream inputStream = Files.newInputStream(file)) {
64-
JsonUtil.loadStringMapFromJson(inputStream, result::put);
65-
MagicLib.getLogger().debug("Loaded language file {}.", file);
89+
private PackAccessor adaptPack(PackResources packResources) {
90+
return this.adaptPack(packResources, false);
91+
}
92+
93+
private PackAccessor adaptPack(PackResources packResources, boolean recursive) {
94+
if (packResources instanceof PackAccessor) {
95+
return (PackAccessor) packResources;
96+
}
97+
98+
//#if MC < 11903
99+
if (packResources instanceof LegacyPackResourcesAdapterAccessor) {
100+
try (PackResources v3PackRes = ((LegacyPackResourcesAdapterAccessor) packResources).magiclib$getSource()) {
101+
return this.adaptPack(v3PackRes, true);
102+
} catch (Exception e) {
103+
MagicLib.getLogger().error("Failed to unpack v3adapter {}.", packResources.getName(), e);
104+
return null;
105+
}
106+
}
107+
//#endif
108+
109+
//#if 11903 > MC && MC > 11404
110+
if (packResources instanceof PackResourcesAdapterV4Accessor) {
111+
try (PackResources v4PackRes = ((PackResourcesAdapterV4Accessor) packResources).magiclib$getPack()) {
112+
return this.adaptPack(v4PackRes, true);
66113
} catch (Exception e) {
67-
MagicLib.getLogger().error("Failed to load language file {}.", file, e);
114+
MagicLib.getLogger().error("Failed to unpack v4adapter {}.", packResources.getName(), e);
115+
return null;
68116
}
69-
});
117+
}
118+
//#endif
70119

71-
return result;
120+
if (!recursive) {
121+
MagicLib.getLogger().error("Failed to unpack {}.", packResources.getName());
122+
}
123+
124+
return null;
125+
}
126+
127+
private void initLanguageMap(PackAccessor pack) {
128+
if (pack instanceof FolderPackResources) {
129+
this.loadFromFolderPack(pack.magiclib$getFile().toPath());
130+
} else if (pack instanceof FilePackResources) {
131+
this.loadFromZipPack(pack.magiclib$getFile());
132+
} else {
133+
MagicLib.getLogger().error("Unknown resource the type of pack {}.", ((PackResources) pack).getName());
134+
}
135+
}
136+
137+
@SuppressWarnings("UnstableApiUsage")
138+
private void loadFromZipPack(File file) {
139+
try (ZipFile zipFile = new ZipFile(file)) {
140+
for (ZipEntry entry : Collections.list(zipFile.entries())) {
141+
try (InputStream inputStream = zipFile.getInputStream(entry)) {
142+
if (JarLanguageProvider.loadFromEntry(entry, inputStream, languageCode ->
143+
this.languageMap.computeIfAbsent(languageCode, k -> Maps.newConcurrentMap()))) {
144+
MagicLib.getLogger().debug("Loaded language file {} from {}.", entry.getName(), file.getName());
145+
}
146+
} catch (IOException e) {
147+
MagicLib.getLogger().error("Failed to load language file {} from {}.",
148+
entry.getName(), zipFile.getName(), e);
149+
}
150+
}
151+
} catch (IOException e) {
152+
MagicLib.getLogger().error("Failed to load language file from {}.", file.getName(), e);
153+
}
72154
}
73155

74-
private void updateFileList(Path path) {
156+
@SuppressWarnings("UnstableApiUsage")
157+
private void loadFromFolderPack(Path path) {
75158
try {
76-
Files.walkFileTree(path, new FileLanguageProvider.LanguageFileVisitor(path, this.files, true));
77-
} catch (IOException ignore) {
78-
// ignore.
159+
Map<String, List<Path>> files = Maps.newConcurrentMap();
160+
Files.walkFileTree(path, new LanguageFileVisitor(path, files, true));
161+
162+
for (Entry<String, List<Path>> entry : files.entrySet()) {
163+
Map<String, String> map = this.languageMap.computeIfAbsent(entry.getKey(), k -> Maps.newConcurrentMap());
164+
165+
for (Path p : entry.getValue()) {
166+
try (InputStream inputStream = Files.newInputStream(p)) {
167+
JsonUtil.loadStringMapFromJson(inputStream, map::put);
168+
MagicLib.getLogger().debug("Loaded language file {}.", path);
169+
}
170+
}
171+
}
172+
} catch (Exception e) {
173+
MagicLib.getLogger().error("Failed to load language file {}.", path, e);
79174
}
80175
}
81176
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package top.hendrixshen.magiclib.mixin.minecraft.accessor;
2+
3+
import net.minecraft.client.resources.LegacyPackResourcesAdapter;
4+
import net.minecraft.server.packs.PackResources;
5+
6+
import org.spongepowered.asm.mixin.Mixin;
7+
import org.spongepowered.asm.mixin.gen.Accessor;
8+
9+
@Mixin(LegacyPackResourcesAdapter.class)
10+
public interface LegacyPackResourcesAdapterAccessor {
11+
@Accessor("source")
12+
PackResources magiclib$getSource();
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package top.hendrixshen.magiclib.mixin.minecraft.accessor;
2+
3+
import net.minecraft.client.resources.PackResourcesAdapterV4;
4+
import net.minecraft.server.packs.PackResources;
5+
6+
import org.spongepowered.asm.mixin.Mixin;
7+
import org.spongepowered.asm.mixin.gen.Accessor;
8+
9+
@Mixin(PackResourcesAdapterV4.class)
10+
public interface PackResourcesAdapterV4Accessor {
11+
@Accessor("pack")
12+
PackResources magiclib$getPack();
13+
}

magiclib-minecraft-api/src/main/resources/magiclib-minecraft-api.mixins.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
},
1010
"client": [
1111
"accessor.GuiGraphicsAccessor",
12+
"accessor.LegacyPackResourcesAdapterAccessor",
1213
"accessor.Matrix4fAccessor",
14+
"accessor.PackResourcesAdapterV4Accessor",
1315
"accessor.QuaternionAccessor",
1416
"accessor.ScreenAccessor",
1517
"accessor.StringSplitterAccessor",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package top.hendrixshen.magiclib.mixin.minecraft.accessor;
2+
3+
import org.spongepowered.asm.mixin.Mixin;
4+
5+
import top.hendrixshen.magiclib.api.preprocess.DummyClass;
6+
7+
@Mixin(DummyClass.class)
8+
public interface PackResourcesAdapterV4Accessor {
9+
}

0 commit comments

Comments
 (0)