From 85f9b1012cb7f15c3e6bd802337385fd87ceb39b Mon Sep 17 00:00:00 2001 From: matyrobbrt <65940752+Matyrobbrt@users.noreply.github.com> Date: Tue, 27 Dec 2022 21:40:06 +0200 Subject: [PATCH] Remove resource caching (#9254) --- .../packs/VanillaPackResources.java.patch | 58 -- .../MultiPackResourceManager.java.patch | 10 - .../extensions/IForgePackResources.java | 4 - .../resource/DelegatingPackResources.java | 19 +- .../resource/PathPackResources.java | 22 - .../resource/ResourceCacheManager.java | 594 ------------------ 6 files changed, 2 insertions(+), 705 deletions(-) delete mode 100644 patches/minecraft/net/minecraft/server/packs/VanillaPackResources.java.patch delete mode 100644 patches/minecraft/net/minecraft/server/packs/resources/MultiPackResourceManager.java.patch delete mode 100644 src/main/java/net/minecraftforge/resource/ResourceCacheManager.java diff --git a/patches/minecraft/net/minecraft/server/packs/VanillaPackResources.java.patch b/patches/minecraft/net/minecraft/server/packs/VanillaPackResources.java.patch deleted file mode 100644 index 2590c4a518..0000000000 --- a/patches/minecraft/net/minecraft/server/packs/VanillaPackResources.java.patch +++ /dev/null @@ -1,58 +0,0 @@ ---- a/net/minecraft/server/packs/VanillaPackResources.java -+++ b/net/minecraft/server/packs/VanillaPackResources.java -@@ -32,6 +_,19 @@ - this.f_10314_ = p_250468_; - this.f_244169_ = p_248798_; - this.f_244459_ = p_251106_; -+ this.cacheManager = new net.minecraftforge.resource.ResourceCacheManager(false, "indexVanillaPackCachesOnThread", -+ (packType, namespace) -> { -+ List validPaths = new java.util.ArrayList<>(); -+ -+ for (Path basePath : this.f_244459_.getOrDefault(packType, List.of())) { -+ Path path = basePath.resolve(namespace); -+ if (Files.exists(path) && PathPackResources.m_246877_(path)) { -+ validPaths.add(path); -+ } -+ } -+ -+ return validPaths; -+ }); - } - - @Nullable -@@ -66,6 +_,14 @@ - public void m_8031_(PackType p_248974_, String p_248703_, String p_250848_, PackResources.ResourceOutput p_249668_) { - FileUtil.m_245538_(p_250848_).get().ifLeft((p_248228_) -> { - List list = this.f_244459_.get(p_248974_); -+ // TODO 1.19.3: Fix resource caching to actually work and produce valid results -SS -+ // if (net.minecraftforge.resource.ResourceCacheManager.shouldUseCache() && this.cacheManager.hasCached(p_248974_, p_248703_)) { -+ // for (Path rootPath : list) { -+ // this.cacheManager.listResources(p_248974_, p_248703_, FileUtil.resolvePath(rootPath, p_248228_), this::getResource, p_249668_); -+ // } -+ // return; -+ // } -+ - int i = list.size(); - if (i == 1) { - m_246310_(p_249668_, p_248703_, list.get(0), p_248228_); -@@ -161,5 +_,20 @@ - return new Resource(this, p_248221_); - }); - }; -+ } -+ -+ private final net.minecraftforge.resource.ResourceCacheManager cacheManager; -+ -+ @Override -+ public void initForNamespace(final String namespace) { -+ if (net.minecraftforge.resource.ResourceCacheManager.shouldUseCache()) -+ this.cacheManager.index(namespace); -+ } -+ -+ @Override -+ public void init(final PackType packType) { -+ // Forge: Init vanilla namespaces -+ initForNamespace("minecraft"); -+ initForNamespace("realms"); - } - } diff --git a/patches/minecraft/net/minecraft/server/packs/resources/MultiPackResourceManager.java.patch b/patches/minecraft/net/minecraft/server/packs/resources/MultiPackResourceManager.java.patch deleted file mode 100644 index a61cdc96cf..0000000000 --- a/patches/minecraft/net/minecraft/server/packs/resources/MultiPackResourceManager.java.patch +++ /dev/null @@ -1,10 +0,0 @@ ---- a/net/minecraft/server/packs/resources/MultiPackResourceManager.java -+++ b/net/minecraft/server/packs/resources/MultiPackResourceManager.java -@@ -24,6 +_,7 @@ - public MultiPackResourceManager(PackType p_203797_, List p_203798_) { - this.f_203795_ = List.copyOf(p_203798_); - Map map = new HashMap<>(); -+ p_203798_.forEach(resourcePack -> resourcePack.init(p_203797_)); - List list = p_203798_.stream().flatMap((p_215471_) -> { - return p_215471_.m_5698_(p_203797_).stream(); - }).distinct().toList(); diff --git a/src/main/java/net/minecraftforge/common/extensions/IForgePackResources.java b/src/main/java/net/minecraftforge/common/extensions/IForgePackResources.java index eccf3465f5..57a99d71ec 100644 --- a/src/main/java/net/minecraftforge/common/extensions/IForgePackResources.java +++ b/src/main/java/net/minecraftforge/common/extensions/IForgePackResources.java @@ -22,10 +22,6 @@ public interface IForgePackResources return false; } - default void initForNamespace(final String nameSpace) {} - - default void init(final PackType packType) {} - /** * Gets a collection of {@code PackResource} instances nested inside this pack. * Used to merge several packs into one entry in the resource pack selection UI without diff --git a/src/main/java/net/minecraftforge/resource/DelegatingPackResources.java b/src/main/java/net/minecraftforge/resource/DelegatingPackResources.java index 66fb3974f3..a07b7cfaac 100644 --- a/src/main/java/net/minecraftforge/resource/DelegatingPackResources.java +++ b/src/main/java/net/minecraftforge/resource/DelegatingPackResources.java @@ -29,8 +29,8 @@ public class DelegatingPackResources extends AbstractPackResources { private final PackMetadataSection packMeta; private final List delegates; - private Map> namespacesAssets; - private Map> namespacesData; + private final Map> namespacesAssets; + private final Map> namespacesData; public DelegatingPackResources(String packId, boolean isBuiltin, PackMetadataSection packMeta, List packs) { @@ -41,21 +41,6 @@ public class DelegatingPackResources extends AbstractPackResources this.namespacesData = this.buildNamespaceMap(PackType.SERVER_DATA, delegates); } - @Override - public void initForNamespace(final String nameSpace) - { - this.delegates.forEach(delegate -> delegate.initForNamespace(nameSpace)); - } - - @Override - public void init(final PackType packType) - { - this.delegates.forEach(packResources -> packResources.init(packType)); - - this.namespacesAssets = buildNamespaceMap(PackType.CLIENT_RESOURCES, delegates); - this.namespacesData = buildNamespaceMap(PackType.SERVER_DATA, delegates); - } - private Map> buildNamespaceMap(PackType type, List packList) { Map> map = new HashMap<>(); diff --git a/src/main/java/net/minecraftforge/resource/PathPackResources.java b/src/main/java/net/minecraftforge/resource/PathPackResources.java index c0948b11aa..8909661ab0 100644 --- a/src/main/java/net/minecraftforge/resource/PathPackResources.java +++ b/src/main/java/net/minecraftforge/resource/PathPackResources.java @@ -37,8 +37,6 @@ public class PathPackResources extends AbstractPackResources { private static final Logger LOGGER = LogUtils.getLogger(); private final Path source; - private final ResourceCacheManager cacheManager = new ResourceCacheManager(true, "indexModPackCachesOnThread", - (packType, namespace) -> List.of(resolve(packType.getDirectory(), namespace).toAbsolutePath())); /** * Constructs a java.nio.Path-based resource pack. @@ -54,21 +52,6 @@ public class PathPackResources extends AbstractPackResources this.source = source; } - @Override - public void initForNamespace(final String namespace) - { - if (ResourceCacheManager.shouldUseCache()) - { - this.cacheManager.index(namespace); - } - } - - @Override - public void init(final PackType packType) - { - getNamespacesFromDisk(packType).forEach(this::initForNamespace); - } - /** * Returns the source path containing the resource pack. * This is used for error display. @@ -116,11 +99,6 @@ public class PathPackResources extends AbstractPackResources @Override public Set getNamespaces(PackType type) { - if (ResourceCacheManager.shouldUseCache()) - { - return this.cacheManager.getNamespaces(type); - } - return getNamespacesFromDisk(type); } diff --git a/src/main/java/net/minecraftforge/resource/ResourceCacheManager.java b/src/main/java/net/minecraftforge/resource/ResourceCacheManager.java deleted file mode 100644 index a19024780b..0000000000 --- a/src/main/java/net/minecraftforge/resource/ResourceCacheManager.java +++ /dev/null @@ -1,594 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.minecraftforge.resource; - -import com.electronwill.nightconfig.core.ConfigFormat; -import com.electronwill.nightconfig.core.ConfigSpec; -import com.electronwill.nightconfig.core.file.CommentedFileConfig; -import com.electronwill.nightconfig.core.io.ParsingException; -import com.electronwill.nightconfig.core.io.WritingMode; -import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterators; -import com.google.common.collect.Maps; -import com.mojang.logging.LogUtils; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import net.minecraft.Util; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.PackResources; -import net.minecraft.server.packs.PackType; -import net.minecraft.server.packs.resources.IoSupplier; -import net.minecraftforge.common.ForgeConfigSpec; -import net.minecraftforge.fml.loading.FMLPaths; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.Marker; -import org.slf4j.MarkerFactory; - -/** - * Cache manager for resources. - *

- * This class handles caching the resource listing calls on a pack, pack type and namespace level. - */ -@ApiStatus.Internal -public class ResourceCacheManager -{ - /** - * Logging marker to handle the hiding of the entries from the debug file. - */ - private static final Marker RESOURCE_CACHE = MarkerFactory.getMarker("RESOURCE-CACHE"); - private static final Joiner SLASH_JOINER = Joiner.on('/'); - - /** - * Indicates if the underlying namespaced managers need to support reloading. - * Disabled for the (in-jar / downloaded) default vanilla pack. - */ - private final boolean supportsReloading; - - /** - * The config key which can be used to check if the indexing of the file-tree should happen off-thread or on-thread and block the process accordingly. - */ - private final String indexOnThreadConfigurationKey; - - /** - * The path builder (different users have different requirements for how they want to handle this) - */ - private final BiFunction> pathBuilder; - - /** - * The individual sub managers by pack type and namespace - */ - private final Map managersByNamespace = Maps.newConcurrentMap(); - - /** - * Creates a new instance of a resource cache manager. - * - * @param supportsReloading True to make the namespace specific managers support reloading the cache. - * @param indexOffThreadConfig True to index the file-tree off-thread. - * @param pathBuilder The path builder to use. - */ - @Deprecated(since = "1.19.2", forRemoval = true) - public ResourceCacheManager(final boolean supportsReloading, final ForgeConfigSpec.BooleanValue indexOffThreadConfig, final BiFunction> pathBuilder) - { - this.supportsReloading = supportsReloading; - this.indexOnThreadConfigurationKey = Iterators.getLast(indexOffThreadConfig.getPath().iterator()); - this.pathBuilder = pathBuilder; - } - - public ResourceCacheManager(final boolean supportsReloading, final String indexOnThreadConfigurationKey, final BiFunction> pathBuilder) - { - this.supportsReloading = supportsReloading; - this.indexOnThreadConfigurationKey = indexOnThreadConfigurationKey; - this.pathBuilder = pathBuilder; - } - - /** - * Indicates if the caching system is enabled or not. - * - * @return {@code true} if the caching system is enabled, {@code false} otherwise. - */ - public static boolean shouldUseCache() - { - // TODO 1.19.3: Fix resource listing cache -SS - return false; // ResourceManagerBootCacheConfigurationHandler.getInstance().getConfigValue("cachePackAccess", true); - } - - /** - * Indicates if this cache manager requires the indexing of the file-tree to happen on the main thread. - * - * @return {@code true} if the indexing of the file-tree should happen on the main thread, {@code false} otherwise. - */ - public boolean shouldIndexOnThread() - { - return ResourceManagerBootCacheConfigurationHandler.getInstance().getConfigValue(this.indexOnThreadConfigurationKey, false); - } - - /** - * Indicates if this cache manager requires the indexing of the file-tree to happen off the main thread. - * - * @return {@code true} if the indexing of the file-tree should happen off the main thread, {@code true} otherwise. - */ - @Deprecated(forRemoval = true, since = "1.19.2") - public boolean shouldIndexOffThread() - { - return !this.shouldIndexOnThread(); - } - - /** - * Invoked to trigger indexing of a given namespaces resources. - * - * @param namespace The namespace to index. - */ - public void index(final String namespace) - { - for (final PackType packType : PackType.values()) - { - final PackTypeAndNamespace key = new PackTypeAndNamespace(packType, namespace); // Make a key. - if (managersByNamespace.containsKey(key) && !supportsReloading) // If we do not support reloading we just skip this. - { - return; - } - - // Create a new manager, overriding the previous one if it exists. - final NamespacedResourceCacheManager newManager = new NamespacedResourceCacheManager(packType, namespace, this.shouldIndexOnThread(), pathBuilder, this::createWalkingStream); - this.managersByNamespace.put(key, newManager); - - // Index the inner manager (which will for sure exist, and will know the pack type and namespace already) - newManager.index(); - } - } - - /** - * Handling method to create a new stream of paths in the target directory, which can then be walked. - * The caller needs to close the stream manually. - * - * @param path The path to walk. - * @return A stream of paths which reside in the given path. - * @throws IOException Thrown when the path does not exist or reading of path information failed. - */ - @SuppressWarnings("resource") - // We close this automatically later on when the result is actually used. This is just a factory. - private Stream createWalkingStream(final Path path) throws IOException - { - return Files.walk(path) - .filter(resourcePath -> !resourcePath.toString().endsWith(".mcmeta")); - } - - /** - * Lists the cached resources for the given pack type, namespace, path prefix and filter. - * - * @param type The type of pack to look up in (data or resource) - * @param resourceNamespace The namespace of the resources to look up. - * @param inputPath The input path prefix to check for. - * @param resourceFunction The function that turns a resource location into an input stream supplier. - * @param resourceOutput The resource output to accept resources. - */ - public void listResources(final PackType type, final String resourceNamespace, final Path inputPath, final BiFunction> resourceFunction, - final PackResources.ResourceOutput resourceOutput) - { - final PackTypeAndNamespace key = new PackTypeAndNamespace(type, resourceNamespace); - final NamespacedResourceCacheManager manager = managersByNamespace.get(key); - - // Check if we even have a cached manager with the given namespace, else return an empty collection. - if (manager == null) - { - return; - } - - // Request the data from the manager. - manager.listResources(inputPath, loc -> resourceFunction.apply(type, loc), resourceOutput); - } - - /** - * Looks up all already indexed namespaces. - * Note even though a namespace might be returned from this method, it does not mean that the indexing has completed - * on that namespace if it is running off-thread. - * - * @param type The type of the pack to get the namespaces for. - * @return The namespaces which are known for the given pack type. - */ - public Set getNamespaces(final PackType type) - { - return managersByNamespace.keySet().stream() - .filter(key -> key.packType() == type) - .map(PackTypeAndNamespace::namespace) - .collect(Collectors.toSet()); - } - - /** - * Indicates if the given namespace is completely cached for the given pack type. - * - * @param packType The pack type to check. - * @param namespace The namespace to check. - * @return True if the namespace is completely cached for the given pack type. - */ - public boolean hasCached(final PackType packType, final String namespace) - { - final PackTypeAndNamespace key = new PackTypeAndNamespace(packType, namespace); - final NamespacedResourceCacheManager manager = managersByNamespace.get(key); - return manager != null && manager.cacheLoaded(); - } - - /** - * Record for the delegated namespaced manager map. - * - * @param packType The pack type. - * @param namespace The namespace. - */ - private record PackTypeAndNamespace(PackType packType, String namespace) - { - } - - /** - * Record for the individual cache entries. - * - * @param packType The pack type that the resource resides in. - * @param namespace The namespace that the resource resides in. - * @param path The path in the pack that the resource resides in. - * @param resourceLocation The resource location for the resource. - */ - private record ResourceCacheEntry(PackType packType, String namespace, Path path, - ResourceLocation resourceLocation) - { - } - - /** - * A namespaced and pack type specific cache which the {@link ResourceCacheManager} can delegate the handling to. - */ - private static class NamespacedResourceCacheManager - { - private static final Logger LOGGER = LogUtils.getLogger(); - - /** - * The pack type this manager is responsible for. - */ - private final PackType packType; - /** - * The namespace this manager is responsible for. - */ - private final String namespace; - /** - * Indicates if the indexing should run on-thread. - */ - private final boolean indexOnThread; - /** - * The path builder to use. - */ - private final BiFunction> pathBuilder; - /** - * The path walker stream factory to use. - */ - private final PathWalkerFactory pathFinder; - - /** - * The cache entries for this manager. - */ - private final Map> entriesByPathPrefix = Maps.newConcurrentMap(); - /** - * Indicates if the cache has been loaded successfully. - */ - private final AtomicBoolean cacheLoaded = new AtomicBoolean(false); - - /** - * Creates a new namespaced resource cache manager. - * - * @param packType The pack type that this manager handles. - * @param namespace The namespace that this manager handles. - * @param indexOnThread True to enable indexing on-thread. - * @param pathBuilder The path builder to use. - * @param pathFinder The path walker stream factory to use. - */ - private NamespacedResourceCacheManager(final PackType packType, final String namespace, final boolean indexOnThread, BiFunction> pathBuilder, final PathWalkerFactory pathFinder) - { - this.packType = packType; - this.namespace = namespace; - this.indexOnThread = indexOnThread; - this.pathBuilder = pathBuilder; - this.pathFinder = pathFinder; - } - - /** - * Triggers indexing of this manager. - */ - public void index() - { - if (indexOnThread) - { - // Run on-thread - doIndex(); - } - else - { - // Run off-thread. - CompletableFuture.runAsync( - this::doIndex, - Util.backgroundExecutor() - ); - } - } - - /** - * Performs the actual indexing of this manager. - * Can be invoked on- or off-thread. - */ - private void doIndex() - { - LOGGER.debug(RESOURCE_CACHE, "Indexing resources for pack type {} and namespace {}, on thread: {}", packType, namespace, Thread.currentThread().getName()); - - // Get the path(s) to the root of the namespace in the current pack. - final List rootPaths = pathBuilder.apply(packType, namespace); - - rootPaths.forEach(this::doIndex); - } - - private void doIndex(Path rootPath) - { - // Stream element resource that combines a normalized path and a joined path using the "/" as separator. - record PathWithLocationPath(Path path, String locationPath) - { - } - - // Build a walkable stream, process it - try (final Stream paths = pathFinder.createWalkingStream(rootPath)) - { - paths.parallel() // Run the stream in parallel - .filter(Predicate.not(rootPath::equals)) - .map(rootPath::relativize) // Relative to the given root. - .map(path -> new PathWithLocationPath(path, SLASH_JOINER.join(path))) // Create a common hierarchy. - .filter(path -> ResourceLocation.isValidPath(path.locationPath())) // Only process valid paths - .map(path -> new ResourceCacheEntry(packType, namespace, path.path(), new ResourceLocation(namespace, path.locationPath()))) // Create a cache entry. - .forEach(this::injectIntoCache); // Inject the entry into the cache. - } - catch (NoSuchFileException noSuchFileException) - { - LOGGER.debug(RESOURCE_CACHE, "Failed to cache resources, the directory does not exist!", noSuchFileException); - } - catch (IOException ioException) - { - LOGGER.error(RESOURCE_CACHE, "Failed to cache resources, some stuff might be missing!", ioException); - } - catch (Exception exception) - { - LOGGER.error(RESOURCE_CACHE, "Failed to cache resources, some stuff might be missing! Unknown exception!", exception); - } - finally - { - cacheLoaded.set(true); - } - } - - /** - * Injects the given cache entry into the cache. - * By first injecting it into the parent of the entry (so the directory it resides in) and then recursively walking up. - * - * @param entry The entry to inject into the cache, must not be null. - */ - private void injectIntoCache(final ResourceCacheEntry entry) - { - injectIntoCache(entry.path().getParent(), entry); - } - - /** - * Injects the given cache entry into the cache, recursively into all of its parent paths. - * - * @param parentPath The parent path to inject into the cache, can be null. Null will be treated as the root path. - * @param entry The entry to inject into the cache, must not be null. - */ - private void injectIntoCache(@Nullable final Path parentPath, final ResourceCacheEntry entry) - { - String pathEntry; - if (parentPath == null) - { - // We have null, generally this only happens on the root directory. - pathEntry = ""; - } - else - { - // Use the parent paths, we can not depend on a toString call here, since we need the / as separator. - pathEntry = Joiner.on("/").join(parentPath); - } - - // Inject into the cache, we can use a normal array list here since we have guarded against cache access later on. - this.entriesByPathPrefix.computeIfAbsent(pathEntry, e -> new CopyOnWriteArrayList<>()).add(entry); - - // Recursively walk to the top, while preventing duplicate entries. - if (parentPath != null && !pathEntry.isEmpty()) - { - this.injectIntoCache(parentPath.getParent(), entry); - } - } - - /** - * Looks up resources in the given input path prefix. - * - * @param inputPath The input path prefix to look in. - * @param resourceFunction The function that turns a resource location into an input stream supplier. - * @param resourceOutput The resource output to accept resources. - */ - public void listResources(final Path inputPath, Function> resourceFunction, final PackResources.ResourceOutput resourceOutput) - { - // We only have a cache once the atomic flag has been set. - if (!cacheLoaded()) - { - return; - } - - // Use the input path prefix, we can not depend on a toString call here, since we need the / as separator. - final String pathEntry = SLASH_JOINER.join(inputPath); - - // Since we inject into the cache recursively we can now just grab the map entry that is the prefix and loop over its values. - // We use getOrDefault with a stream combination since the returned list needs to be mutable. - List entries = this.entriesByPathPrefix.get(pathEntry); - if (entries == null) - return; - - entries.forEach(cacheEntry -> resourceOutput.accept(cacheEntry.resourceLocation(), resourceFunction.apply(cacheEntry.resourceLocation()))); - } - - /** - * Indicates if the cache of this manager has been loaded. - * - * @return True if the cache has been loaded, false otherwise. - */ - public boolean cacheLoaded() - { - return cacheLoaded.get(); - } - } - - /** - * Functional callback interface to get a walkable stream of paths. - * Supports throwing {@link IOException} if the stream can not be created. - */ - @FunctionalInterface - private interface PathWalkerFactory - { - /** - * Create a new walkable stream of paths. - * The stream will be closed by the caller. - * - * @param path The path to create the stream for. - * @return A new stream of paths that are children (potentially several generations deep) of the given path. - * @throws IOException If the stream can not be created. - */ - Stream createWalkingStream(Path path) throws IOException; - } - - /** - * Class to handle the reading, initial creation, and watching of the boot configuration file, for the resource cache manager. - */ - private static final class ResourceManagerBootCacheConfigurationHandler - { - /** - * The logger for the {@link ResourceManagerBootCacheConfigurationHandler} - */ - private static final Logger LOGGER = LogUtils.getLogger(); - - /** - * The path to the boot configuration file. - */ - private static final Path CONFIG_PATH = FMLPaths.CONFIGDIR.get().resolve("forge-resource-caching.toml"); - - /** - * The configuration specification for the resource cache configuration. - */ - private static final ConfigSpec configSpec = new ConfigSpec(); - static { - configSpec.define("cacheResources", Boolean.TRUE); - configSpec.define("indexVanillaPackCachesOnThread", Boolean.FALSE); - configSpec.define("indexModPackCachesOnThread", Boolean.FALSE); - } - - /** - * The current instance of the handler. - * @implNote This needs to be the last static field (or at least below the config spec and the config path, else some of them create NPEs because they are not initialized yet) - */ - private static final ResourceManagerBootCacheConfigurationHandler INSTANCE = new ResourceManagerBootCacheConfigurationHandler(); - - /** - * The configuration handle for the resource cache configuration. - */ - private final CommentedFileConfig configurationHandle; - - /** - * Creates a new instance of the handler. - * Registers the watchdog thread. - */ - private ResourceManagerBootCacheConfigurationHandler() - { - this.configurationHandle = createConfiguration(); - } - - /** - * Creates the configuration handle for the resource cache configuration. - */ - private static CommentedFileConfig createConfiguration() - { - final CommentedFileConfig configData = CommentedFileConfig.builder(CONFIG_PATH).sync() - .onFileNotFound(ResourceManagerBootCacheConfigurationHandler::onConfigFileNotFound) - .autosave() - .autoreload() - .concurrent() - .writingMode(WritingMode.REPLACE) - .build(); - try - { - configData.load(); - } - catch (ParsingException e) - { - throw new RuntimeException("Failed to load Force Resource Cache Configuration from %s".formatted(CONFIG_PATH), e); - } - if (!configSpec.isCorrect(configData)) { - LOGGER.warn("Configuration file {} is not correct. Correcting", CONFIG_PATH); - configSpec.correct(configData, (action, path, incorrectValue, correctedValue) -> - LOGGER.warn("Incorrect key {} was corrected from {} to {}", path, incorrectValue, correctedValue)); - } - - configData.save(); - - return configData; - } - - private static boolean onConfigFileNotFound(Path file, ConfigFormat configFormat) throws IOException { - Files.write(file, ImmutableList.of( - "# This TOML configuration file controls the resource caching system which is used before the mod loading environment starts.", - "# This file is read by the Forge boot loader, and is not used by the game itself.", - "#", - "# Set this to false to disable the resource cache. This will cause the game to scan the resource packs everytime it needs a list of resources.", - "cacheResources=true", - "", - "# Set this to true to force the caching of vanilla resources to happen on the main thread.", - "indexVanillaPackCachesOnThread=false", - "", - "# Set this to true to force the caching of mod resources to happen on the main thread.", - "indexModPackCachesOnThread=false" - ), - StandardOpenOption.CREATE_NEW); - - return true; - } - - /** - * Gives access to the current singleton instance of the handler. - * - * @return The instance of this handler. - */ - public static ResourceManagerBootCacheConfigurationHandler getInstance() - { - return INSTANCE; - } - - /** - * Reads a config value from the current configuration file. - * - * @param configKey The key of the config value to read. - * @param defaultValue The default value to return if the config value is not present. - * @return The value of the config value, or the default value if it is not present. - */ - private boolean getConfigValue(final String configKey, final boolean defaultValue) - { - return this.configurationHandle.getOptional(configKey).orElse(defaultValue); - } - } -}