Skip to content
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
Expand Up @@ -20,9 +20,8 @@
import com.hfstudio.guidenh.guide.compiler.ParsedGuidePage;
import com.hfstudio.guidenh.guide.internal.datadriven.DataDrivenGuideLoader;
import com.hfstudio.guidenh.guide.internal.datadriven.GuidePageResourceSelector;
import com.hfstudio.guidenh.guide.internal.localization.GuideLanguageIndex;
import com.hfstudio.guidenh.guide.internal.localization.GuideLocalizedPageSourceResolver;
import com.hfstudio.guidenh.guide.internal.localization.GuidePageLanguageIndex;
import com.hfstudio.guidenh.guide.internal.localization.GuideResourceLanguageIndex;
import com.hfstudio.guidenh.guide.internal.recipe.NeiAnimationTicker;
import com.hfstudio.guidenh.guide.internal.recipe.RecipeCache;
import com.hfstudio.guidenh.guide.internal.resource.GuideResourceAccess;
Expand Down Expand Up @@ -54,8 +53,7 @@ public static void reloadGuides(IResourceManager resourceManager) {
NeiAnimationTicker.clear();
GuidePageTexture.clear();
GuideResourceAccess.clearCache();
GuidePageLanguageIndex.clear();
GuideResourceLanguageIndex.clear();
GuideLanguageIndex.clear();
GuideLatexTextureCache.INSTANCE.clearAll();
GuideSceneStructureCache.global()
.clear();
Expand All @@ -75,7 +73,7 @@ public static void reloadGuides(IResourceManager resourceManager) {
// Build the small runtime localization snapshot while the reload is already in progress.
// This prevents the first Ponder/GameScene opened after reload from synchronously scanning
// every language file on the client thread.
GuideResourceLanguageIndex.warm(language);
GuideLanguageIndex.indexLanguage(language);

for (var guide : GuideRegistry.getAll()) {
var pages = loadPages(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
Expand All @@ -20,6 +21,7 @@
import java.util.concurrent.TimeUnit;

import net.minecraft.util.ResourceLocation;
import net.minecraft.util.StringTranslate;

import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NonNull;
Expand All @@ -28,9 +30,8 @@
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.hfstudio.guidenh.guide.GuidePageChange;
import com.hfstudio.guidenh.guide.compiler.ParsedGuidePage;
import com.hfstudio.guidenh.guide.internal.localization.GuideLanguageIndex;
import com.hfstudio.guidenh.guide.internal.localization.GuideLocalizedPageSourceResolver;
import com.hfstudio.guidenh.guide.internal.localization.GuidePageLanguageIndex;
import com.hfstudio.guidenh.guide.internal.localization.GuideResourceLanguageIndex;
import com.hfstudio.guidenh.guide.internal.util.LangUtil;
import com.hfstudio.guidenh.guide.scene.support.GuideDebugLog;

Expand Down Expand Up @@ -356,8 +357,7 @@ public List<GuidePageChange> takeChanges() {
}
}
if (shouldClearLanguageCache) {
GuidePageLanguageIndex.clear();
GuideResourceLanguageIndex.clear();
GuideLanguageIndex.clear();
}
for (PageReloadRequest request : requests) {
queueReloadedPages(loadAll(request.namespace()));
Expand Down Expand Up @@ -751,7 +751,7 @@ private PageSource resolveActivePageSource(ResourceLocation pageId, String curre
}

try (InputStream input = Files.newInputStream(langFilePath)) {
Map<String, String> entries = GuidePageLanguageIndex.readPageKeys(input);
Map<String, String> entries = readPageKeys(input);
return entries.get(GuideLocalizedPageSourceResolver.buildLangKey(contentRootFolder, pageId));
} catch (IOException e) {
GuideDebugLog
Expand Down Expand Up @@ -821,7 +821,7 @@ private Map<String, String> loadLocalizedSourceOverridesForNamespace(String sour
}

try (InputStream input = Files.newInputStream(langFilePath)) {
return GuidePageLanguageIndex.readPageKeys(input);
return readPageKeys(input);
} catch (IOException e) {
GuideDebugLog
.warn("[GuideNH] [GuideSourceWatcher] Failed to read localized page lang file {}", langFilePath, e);
Expand Down Expand Up @@ -925,4 +925,18 @@ private GuideDevelopmentSourceLayout detectSourceLayout(Path folder) {
private static GuideDevelopmentSourceLayout detectSourceLayout(Path folder, String contentRootFolder) {
return GuideDevelopmentSourceLayout.detect(folder, contentRootFolder);
}

private static Map<String, String> readPageKeys(InputStream input) {
Map<String, String> source = StringTranslate.parseLangFile(input);
Map<String, String> filtered = new LinkedHashMap<>();
if (source.isEmpty()) return filtered;

for (var entry : source.entrySet()) {
if (GuideLanguageIndex.isPageLangKey(entry.getKey())) {
filtered.put(entry.getKey(), entry.getValue());
}
}

return filtered;
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package com.hfstudio.guidenh.guide.internal.datadriven;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
Expand All @@ -18,7 +15,6 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;

import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.AbstractResourcePack;
Expand All @@ -27,7 +23,6 @@
import net.minecraft.client.resources.IResourcePack;
import net.minecraft.client.resources.SimpleReloadableResourceManager;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.StringTranslate;

import org.jetbrains.annotations.Nullable;

Expand All @@ -49,20 +44,10 @@

public class DataDrivenGuideLoader {

// Matches the numeric format placeholders normalized by StringTranslate.parseLangFile.
private static final Pattern NUMERIC_LANG_VARIABLE = Pattern.compile("%(\\d+\\$)?[\\d.]*[df]");

public static final String AUTO_GUIDE_FOLDER = "guidenh";
public static final String LANGUAGE_FOLDER_PREFIX = "_";
private static final String DEFAULT_LANGUAGE = "en_us";

public record PackCandidate(IResourcePack pack, int loadPriority, int order) {

boolean shouldReplace(PackCandidate previous) {
return loadPriority > previous.loadPriority()
|| loadPriority == previous.loadPriority() && order > previous.order();
}
}
public record PackCandidate(IResourcePack pack, ResourceLocation resourceLocation, int order) {}

public record ScanResult(Map<ResourceLocation, MutableGuide> guides, Map<String, LinkedHashSet<String>> pagePaths,
Map<ResourceLocation, Set<String>> discoveredLanguages) {}
Expand All @@ -79,7 +64,6 @@ boolean matches(File root) {
private static final Map<Class<?>, Field> LOOSE_ROOT_FIELDS = new IdentityHashMap<>();
private static volatile List<IResourcePack> lastActiveResourcePacks = List.of();
private static volatile List<IResourcePack> lastResourceManagerResourcePacks = List.of();
private static volatile Map<IResourcePack, Set<String>> lastResourceManagerDomainsByPack = Map.of();
private static final Map<ResourceLocation, List<PackCandidate>> pagePackIndex = new ConcurrentHashMap<>();
private static final Map<ResourceLocation, List<PackCandidate>> assetPackIndex = new ConcurrentHashMap<>();
private static volatile boolean indexReady = false;
Expand All @@ -102,12 +86,7 @@ public static ScanResult scanAndBuildAll(String folder) {
public static ScanResult scanAndBuildAll(String folder, Iterable<? extends IResourcePack> activeResourcePacks) {
var resolvedPacks = toList(activeResourcePacks);

pagePackIndex.clear();
assetPackIndex.clear();
PACK_LANG_FILE_PATHS.clear();
GuideResourcePackScanner.clearCaches();
indexReady = false;
pagePackOrder.set(0);
clearCaches();

var pagePaths = new LinkedHashMap<String, LinkedHashSet<String>>();
var discoveredLanguages = new LinkedHashMap<ResourceLocation, LinkedHashSet<String>>();
Expand Down Expand Up @@ -219,17 +198,18 @@ private static void applyPackScan(IResourcePack resourcePack, String folder, Pac
private static void addPackCandidates(Map<ResourceLocation, List<PackCandidate>> index, IResourcePack resourcePack,
String folder, PackEntry entry) {

index
.computeIfAbsent(
new ResourceLocation(entry.namespace(), folder + "/" + entry.language() + "/" + entry.relativePath()),
k -> new ArrayList<>())
.add(new PackCandidate(resourcePack, entry.loadPriority(), pagePackOrder.getAndIncrement()));
var resourceLocation = new ResourceLocation(
entry.namespace(),
folder + "/" + entry.language() + "/" + entry.relativePath());

index.computeIfAbsent(resourceLocation, k -> new ArrayList<>())
.add(new PackCandidate(resourcePack, resourceLocation, pagePackOrder.getAndIncrement()));

index
.computeIfAbsent(
new ResourceLocation(entry.namespace(), folder + "/" + entry.relativePath()),
k -> new ArrayList<>())
.add(new PackCandidate(resourcePack, entry.loadPriority(), pagePackOrder.getAndIncrement()));
.add(new PackCandidate(resourcePack, resourceLocation, pagePackOrder.getAndIncrement()));
}

public static List<String> getLangFilePaths(IResourcePack resourcePack) {
Expand All @@ -254,106 +234,6 @@ private static File normalizePackRoot(File resourcePackFile) {
.toFile();
}

public static Map<String, String> readLangFile(IResourcePack resourcePack, String entryPath) {
ResourceLocation location = getLangResourceLocation(entryPath);
if (location == null || !resourceExists(resourcePack, location)) return Map.of();
try (var input = resourcePack.getInputStream(location)) {
return StringTranslate.parseLangFile(input);
} catch (IOException | RuntimeException e) {
return Map.of();
}
}

/**
* Reads runtime language entries once for the localization index. Large page-body entries are
* intentionally skipped; they are resolved by {@code GuidePageLanguageIndex} on demand.
*/
public static Map<String, String> readRuntimeLangValues(IResourcePack resourcePack, String entryPath) {
ResourceLocation location = getLangResourceLocation(entryPath);
if (location == null || !resourceExists(resourcePack, location)) return Map.of();
var values = new LinkedHashMap<String, String>();
try (var input = resourcePack.getInputStream(location);
var reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
// StringTranslate.parseLangFile accepts the first '=' separator. Keep the same
// format while avoiding a parser/stream allocation for every entry in large files.
if (line.startsWith("\uFEFF")) line = line.substring(1);
if (line.isEmpty() || line.startsWith("#")) continue;
int separator = line.indexOf('=');
if (separator <= 0) continue;
String key = line.substring(0, separator);
if (key.startsWith("guidenh.page.")) continue;
String value = line.substring(separator + 1);
values.put(
key,
value.indexOf('%') >= 0 ? NUMERIC_LANG_VARIABLE.matcher(value)
.replaceAll("%$1s") : value);
}
} catch (IOException | RuntimeException ignored) {}
return values.isEmpty() ? Map.of() : Map.copyOf(values);
}

public static @Nullable String readLangValue(IResourcePack resourcePack, String entryPath, String key) {
ResourceLocation location = getLangResourceLocation(entryPath);
if (location == null || !resourceExists(resourcePack, location)) return null;
try (var input = resourcePack.getInputStream(location);
var reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith("\uFEFF")) {
line = line.substring(1);
}
if (line.startsWith("#")) continue;
int separator = line.indexOf('=');
if (separator > 0 && key.equals(line.substring(0, separator))) {
// The line has already been split using the same first '=' rule as
// StringTranslate.parseLangFile; avoid reparsing a one-line stream.
String value = line.substring(separator + 1);
return value.indexOf('%') >= 0 ? NUMERIC_LANG_VARIABLE.matcher(value)
.replaceAll("%$1s") : value;
}
}
} catch (IOException | RuntimeException ignored) {}
return null;
}

/** Reads only keys from a language file, avoiding retention of large translated page bodies. */
public static Set<String> readLangKeys(IResourcePack resourcePack, String entryPath) {
ResourceLocation location = getLangResourceLocation(entryPath);
if (location == null || !resourceExists(resourcePack, location)) return Set.of();
var keys = new LinkedHashSet<String>();
try (var input = resourcePack.getInputStream(location);
var reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith("\uFEFF")) line = line.substring(1);
if (line.startsWith("#")) continue;
int separator = line.indexOf('=');
if (separator > 0) keys.add(line.substring(0, separator));
}
} catch (IOException | RuntimeException ignored) {}
return keys.isEmpty() ? Set.of() : Set.copyOf(keys);
}

private static @Nullable ResourceLocation getLangResourceLocation(@Nullable String entryPath) {
if (entryPath == null || !entryPath.endsWith(".lang")) return null;
var afterAssets = entryPath.startsWith("assets/") ? entryPath.substring("assets/".length()) : entryPath;
var firstSlash = afterAssets.indexOf('/');
return firstSlash > 0
? new ResourceLocation(afterAssets.substring(0, firstSlash), afterAssets.substring(firstSlash + 1))
: null;
}

private static boolean resourceExists(IResourcePack resourcePack, ResourceLocation location) {
try {
return resourcePack.resourceExists(location);
} catch (RuntimeException ignored) {
// Third-party resource packs occasionally implement a missing resource as a runtime failure.
return false;
}
}

public static Set<String> discoverPagePaths(ResourceLocation guideId, String folder) {
return discoverPagePaths(guideId, folder, getActiveResourcePacks());
}
Expand Down Expand Up @@ -414,7 +294,6 @@ public static List<IResourcePack> getActiveResourcePacks(IResourceManager resour
var domainsByPack = new IdentityHashMap<IResourcePack, LinkedHashSet<String>>();
addResourceManagerResourcePacks(resourceManager, resourceManagerResourcePacks, domainsByPack);
lastResourceManagerResourcePacks = List.copyOf(resourceManagerResourcePacks);
lastResourceManagerDomainsByPack = freezeDomainsByPack(domainsByPack);
resourcePacks.addAll(resourceManagerResourcePacks);
addConfiguredResourcePacks(resourcePacks);
var resolved = new ArrayList<>(resourcePacks);
Expand Down Expand Up @@ -529,11 +408,6 @@ private static void addResourceManagerResourcePacks(IResourceManager resourceMan
}
}

private static Set<String> getResourceDomains(IResourcePack resourcePack) {
Set<String> cached = lastResourceManagerDomainsByPack.get(resourcePack);
return cached != null ? cached : resourcePack.getResourceDomains();
}

private static Map<IResourcePack, Set<String>> freezeDomainsByPack(
IdentityHashMap<IResourcePack, LinkedHashSet<String>> domainsByPack) {
if (domainsByPack.isEmpty()) return Map.of();
Expand Down
Loading