Skip to content

Fix ResourceLanguageProvider not loading language files correctly #152

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package top.hendrixshen.magiclib.api.i18n;

import java.util.Map;
import java.util.regex.Pattern;

public interface LanguageProvider {
Pattern LANGUAGE_PATH_PATTERN = Pattern.compile("^assets/([\\w-]*)/lang/([a-zA-Z\\d-_]*)\\.json$");

void init();

void reload();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,13 @@
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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

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

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

@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
public @NotNull FileVisitResult preVisitDirectory(Path dir, @NotNull BasicFileAttributes attrs) {
return FileVisitResult.CONTINUE;
}

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

if (!matcher.find()) {
return FileVisitResult.CONTINUE;
}

this.files.computeIfAbsent(matcher.group(2), key ->
Lists.newArrayList()).add(file);
this.files.computeIfAbsent(matcher.group(2), key -> Lists.newArrayList()).add(file);
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
public @NotNull FileVisitResult visitFileFailed(Path file, @NotNull IOException exc) {
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
public @NotNull FileVisitResult postVisitDirectory(Path dir, IOException exc) {
return FileVisitResult.CONTINUE;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package top.hendrixshen.magiclib.impl.i18n.provider;

import com.google.common.collect.Maps;
import com.google.gson.JsonSyntaxException;
import com.google.gson.stream.MalformedJsonException;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.jetbrains.annotations.ApiStatus.Internal;
import org.jetbrains.annotations.NotNull;

import top.hendrixshen.magiclib.MagicLib;
Expand All @@ -18,18 +17,17 @@
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.function.Function;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;

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

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

@Override
public void init() {
Expand Down Expand Up @@ -68,28 +66,34 @@ public Map<String, String> getLanguage(@NotNull String languageCode) {
}

private void loadFromJar(@NotNull JarFile jar) {
for (JarEntry entry : Collections.list(jar.entries())) {
Matcher matcher = languageResourcePattern.matcher(entry.getName());

if (!matcher.find()) {
continue;
}

Map<String, String> language = this.getLanguage(matcher.group(2));

for (ZipEntry entry : Collections.list(jar.entries())) {
try (InputStream inputStream = jar.getInputStream(entry)) {
JsonUtil.loadStringMapFromJson(inputStream, language::put);
MagicLib.getLogger().debug("Loaded language file {} from {}.", entry.getName(), jar.getName());
} catch (Exception e) {
if (e instanceof JsonSyntaxException && e.getCause() instanceof MalformedJsonException) {
MagicLib.getLogger().error("Failed to load language file {} from {}.",
entry.getName(), jar.getName());
continue;
if (JarLanguageProvider.loadFromEntry(entry, inputStream, this::getLanguage)) {
MagicLib.getLogger().debug("Loaded language file {} from {}.", entry.getName(), jar.getName());
}

} catch (IOException e) {
MagicLib.getLogger().error("Failed to load language file {} from {}.",
entry.getName(), jar.getName(), e);
}
}
}

@Internal
public static boolean loadFromEntry(@NotNull ZipEntry entry, InputStream inputStream,
Function<String, Map<String, String>> languageMapGetter) {
Matcher matcher = LanguageProvider.LANGUAGE_PATH_PATTERN.matcher(entry.getName());

if (!matcher.find()) {
return false;
}

Map<String, String> language = languageMapGetter.apply(matcher.group(2));

try {
JsonUtil.loadStringMapFromJson(inputStream, language::put);
return true;
} catch (Exception e) {
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ public static void init() {

@Override
public void postLanguageReload() {
MagicLanguageManager.getInstance().setCurrentCode(Minecraft.getInstance().options.languageCode);
if (!MagicLanguageManager.getInstance().setCurrentCode(Minecraft.getInstance().options.languageCode)) {
MagicLanguageManager.getInstance().reload();
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,68 @@
import lombok.NoArgsConstructor;

import net.minecraft.client.Minecraft;
import net.minecraft.server.packs.FilePackResources;
import net.minecraft.server.packs.PackResources;
import net.minecraft.server.packs.repository.Pack;

// CHECKSTYLE.OFF: ImportOrder
//#if MC > 11902
//$$ import net.minecraft.server.packs.PathPackResources;
//#else
import net.minecraft.server.packs.FolderPackResources;
//#endif
// CHECKSTYLE.ON: ImportOrder

import top.hendrixshen.magiclib.MagicLib;
import top.hendrixshen.magiclib.api.fake.i18n.PackAccessor;
import top.hendrixshen.magiclib.api.i18n.LanguageProvider;
import top.hendrixshen.magiclib.impl.i18n.provider.FileLanguageProvider;
import top.hendrixshen.magiclib.impl.i18n.provider.FileLanguageProvider.LanguageFileVisitor;
import top.hendrixshen.magiclib.impl.i18n.provider.JarLanguageProvider;
import top.hendrixshen.magiclib.util.JsonUtil;

// CHECKSTYLE.OFF: ImportOrder
//#if 11903 > MC && MC > 11404
import top.hendrixshen.magiclib.mixin.minecraft.accessor.PackResourcesAdapterV4Accessor;
//#endif

//#if MC < 11903
import top.hendrixshen.magiclib.mixin.minecraft.accessor.LegacyPackResourcesAdapterAccessor;
//#endif
// CHECKSTYLE.ON: ImportOrder

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

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

private final Map<String, List<Path>> files = Maps.newConcurrentMap();
private final Map<String, Map<String, String>> languageMap = Maps.newConcurrentMap();

@Override
public void init() {
this.languageMap.clear();
Minecraft.getInstance().getResourcePackRepository().getSelectedPacks().stream()
.filter(pack -> pack.getId().startsWith("file"))
.map(Pack::open)
.filter(PackAccessor.class::isInstance)
.map(pack -> ((PackAccessor) pack).magiclib$getFile().toPath())
.forEach(this::updateFileList);
.map(this::adaptPack)
.filter(Objects::nonNull)
.forEach(this::initLanguageMap);
}

@Override
public void reload() {
this.files.clear();
this.init();
}

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

@Override
public Map<String, String> getLanguage(String languageCode) {
Map<String, String> result = Maps.newConcurrentMap();
return this.languageMap.getOrDefault(languageCode, Collections.emptyMap());
}

this.files.getOrDefault(languageCode, Collections.emptyList()).forEach(file -> {
try (InputStream inputStream = Files.newInputStream(file)) {
JsonUtil.loadStringMapFromJson(inputStream, result::put);
MagicLib.getLogger().debug("Loaded language file {}.", file);
private PackAccessor adaptPack(PackResources packResources) {
return this.adaptPack(packResources, false);
}

private PackAccessor adaptPack(PackResources packResources, boolean recursive) {
if (packResources instanceof PackAccessor) {
return (PackAccessor) packResources;
}

//#if MC < 11903
if (packResources instanceof LegacyPackResourcesAdapterAccessor) {
try (PackResources v3PackRes = ((LegacyPackResourcesAdapterAccessor) packResources).magiclib$getSource()) {
return this.adaptPack(v3PackRes, true);
} catch (Exception e) {
MagicLib.getLogger().error("Failed to unpack v3adapter {}.", packResources.getName(), e);
return null;
}
}
//#endif

//#if 11903 > MC && MC > 11404
if (packResources instanceof PackResourcesAdapterV4Accessor) {
try (PackResources v4PackRes = ((PackResourcesAdapterV4Accessor) packResources).magiclib$getPack()) {
return this.adaptPack(v4PackRes, true);
} catch (Exception e) {
MagicLib.getLogger().error("Failed to load language file {}.", file, e);
MagicLib.getLogger().error("Failed to unpack v4adapter {}.", packResources.getName(), e);
return null;
}
});
}
//#endif

return result;
if (!recursive) {
MagicLib.getLogger().error("Failed to unpack {}.", packResources.getName());
}

return null;
}

private void initLanguageMap(PackAccessor pack) {
if (pack instanceof FolderPackResources) {
this.loadFromFolderPack(pack.magiclib$getFile().toPath());
} else if (pack instanceof FilePackResources) {
this.loadFromZipPack(pack.magiclib$getFile());
} else {
MagicLib.getLogger().error("Unknown resource the type of pack {}.", ((PackResources) pack).getName());
}
}

@SuppressWarnings("UnstableApiUsage")
private void loadFromZipPack(File file) {
try (ZipFile zipFile = new ZipFile(file)) {
for (ZipEntry entry : Collections.list(zipFile.entries())) {
try (InputStream inputStream = zipFile.getInputStream(entry)) {
if (JarLanguageProvider.loadFromEntry(entry, inputStream, languageCode ->
this.languageMap.computeIfAbsent(languageCode, k -> Maps.newConcurrentMap()))) {
MagicLib.getLogger().debug("Loaded language file {} from {}.", entry.getName(), file.getName());
}
} catch (IOException e) {
MagicLib.getLogger().error("Failed to load language file {} from {}.",
entry.getName(), zipFile.getName(), e);
}
}
} catch (IOException e) {
MagicLib.getLogger().error("Failed to load language file from {}.", file.getName(), e);
}
}

private void updateFileList(Path path) {
@SuppressWarnings("UnstableApiUsage")
private void loadFromFolderPack(Path path) {
try {
Files.walkFileTree(path, new FileLanguageProvider.LanguageFileVisitor(path, this.files, true));
} catch (IOException ignore) {
// ignore.
Map<String, List<Path>> files = Maps.newConcurrentMap();
Files.walkFileTree(path, new LanguageFileVisitor(path, files, true));

for (Entry<String, List<Path>> entry : files.entrySet()) {
Map<String, String> map = this.languageMap.computeIfAbsent(entry.getKey(), k -> Maps.newConcurrentMap());

for (Path p : entry.getValue()) {
try (InputStream inputStream = Files.newInputStream(p)) {
JsonUtil.loadStringMapFromJson(inputStream, map::put);
MagicLib.getLogger().debug("Loaded language file {}.", path);
}
}
}
} catch (Exception e) {
MagicLib.getLogger().error("Failed to load language file {}.", path, e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package top.hendrixshen.magiclib.mixin.minecraft.accessor;

import net.minecraft.client.resources.LegacyPackResourcesAdapter;
import net.minecraft.server.packs.PackResources;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;

@Mixin(LegacyPackResourcesAdapter.class)
public interface LegacyPackResourcesAdapterAccessor {
@Accessor("source")
PackResources magiclib$getSource();

Check notice on line 12 in magiclib-minecraft-api/src/main/java/top/hendrixshen/magiclib/mixin/minecraft/accessor/LegacyPackResourcesAdapterAccessor.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

magiclib-minecraft-api/src/main/java/top/hendrixshen/magiclib/mixin/minecraft/accessor/LegacyPackResourcesAdapterAccessor.java#L12

The instance method name 'magiclib$getSource' doesn't match '[a-z][a-zA-Z0-9]*'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package top.hendrixshen.magiclib.mixin.minecraft.accessor;

import net.minecraft.client.resources.PackResourcesAdapterV4;
import net.minecraft.server.packs.PackResources;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;

@Mixin(PackResourcesAdapterV4.class)
public interface PackResourcesAdapterV4Accessor {
@Accessor("pack")
PackResources magiclib$getPack();

Check notice on line 12 in magiclib-minecraft-api/src/main/java/top/hendrixshen/magiclib/mixin/minecraft/accessor/PackResourcesAdapterV4Accessor.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

magiclib-minecraft-api/src/main/java/top/hendrixshen/magiclib/mixin/minecraft/accessor/PackResourcesAdapterV4Accessor.java#L12

The instance method name 'magiclib$getPack' doesn't match '[a-z][a-zA-Z0-9]*'
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
},
"client": [
"accessor.GuiGraphicsAccessor",
"accessor.LegacyPackResourcesAdapterAccessor",
"accessor.Matrix4fAccessor",
"accessor.PackResourcesAdapterV4Accessor",
"accessor.QuaternionAccessor",
"accessor.ScreenAccessor",
"accessor.StringSplitterAccessor",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package top.hendrixshen.magiclib.mixin.minecraft.accessor;

import org.spongepowered.asm.mixin.Mixin;

import top.hendrixshen.magiclib.api.preprocess.DummyClass;

@Mixin(DummyClass.class)
public interface PackResourcesAdapterV4Accessor {
}
Loading