Generate resource cache using resource reload workers

This commit is contained in:
embeddedt 2023-05-07 18:08:00 -04:00
parent 65e12016b6
commit b55129a8ca
No known key found for this signature in database
GPG Key ID: A69433EC199B5613

View File

@ -1,16 +1,19 @@
package org.embeddedt.modernfix.resources; package org.embeddedt.modernfix.resources;
import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.PackType; import net.minecraft.server.packs.PackType;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.util.PackTypeHelper; import org.embeddedt.modernfix.util.PackTypeHelper;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
@ -26,6 +29,7 @@ public class PackResourcesCacheEngine {
private final Map<PackType, Set<String>> namespacesByType; private final Map<PackType, Set<String>> namespacesByType;
private final Set<CachedResourcePath> containedPaths; private final Set<CachedResourcePath> containedPaths;
private final EnumMap<PackType, Map<String, List<CachedResourcePath>>> resourceListings; private final EnumMap<PackType, Map<String, List<CachedResourcePath>>> resourceListings;
private CompletableFuture<Void> cacheFuture;
public PackResourcesCacheEngine(Function<PackType, Set<String>> namespacesRetriever, BiFunction<PackType, String, Path> basePathRetriever) { public PackResourcesCacheEngine(Function<PackType, Set<String>> namespacesRetriever, BiFunction<PackType, String, Path> basePathRetriever) {
this.namespacesByType = new EnumMap<>(PackType.class); this.namespacesByType = new EnumMap<>(PackType.class);
@ -36,32 +40,50 @@ public class PackResourcesCacheEngine {
} }
this.containedPaths = new ObjectOpenHashSet<>(); this.containedPaths = new ObjectOpenHashSet<>();
this.resourceListings = new EnumMap<>(PackType.class); this.resourceListings = new EnumMap<>(PackType.class);
CompletableFuture<Void> future = CompletableFuture.completedFuture(null);
Stopwatch watch = Stopwatch.createStarted();
// used for log message
Path debugPath = basePathRetriever.apply(PackType.CLIENT_RESOURCES, "minecraft").toAbsolutePath();
for(PackType type : PackType.values()) { for(PackType type : PackType.values()) {
Collection<String> namespaces = PackTypeHelper.isVanillaPackType(type) ? this.namespacesByType.get(type) : namespacesRetriever.apply(type); Collection<String> namespaces = PackTypeHelper.isVanillaPackType(type) ? this.namespacesByType.get(type) : namespacesRetriever.apply(type);
ImmutableMap.Builder<String, List<CachedResourcePath>> packTypedMap = ImmutableMap.builder(); future = future.thenRunAsync(() -> {
for(String namespace : namespaces) { ImmutableMap.Builder<String, List<CachedResourcePath>> packTypedMap = ImmutableMap.builder();
try { for(String namespace : namespaces) {
ImmutableList.Builder<CachedResourcePath> namespacedList = ImmutableList.builder(); try {
Path root = basePathRetriever.apply(type, namespace).toAbsolutePath(); ImmutableList.Builder<CachedResourcePath> namespacedList = ImmutableList.builder();
String[] prefix = new String[] { type.getDirectory(), namespace }; Path root = basePathRetriever.apply(type, namespace).toAbsolutePath();
try (Stream<Path> stream = Files.walk(root)) { String[] prefix = new String[] { type.getDirectory(), namespace };
stream try (Stream<Path> stream = Files.walk(root)) {
.map(path -> root.relativize(path.toAbsolutePath())) stream
.filter(PackResourcesCacheEngine::isValidCachedResourcePath) .map(path -> root.relativize(path.toAbsolutePath()))
.forEach(path -> { .filter(PackResourcesCacheEngine::isValidCachedResourcePath)
CachedResourcePath cachedPath = new CachedResourcePath(prefix, path); .forEach(path -> {
this.containedPaths.add(cachedPath); CachedResourcePath cachedPath = new CachedResourcePath(prefix, path);
if(!cachedPath.getFileName().endsWith(".mcmeta")) synchronized (this.containedPaths) {
namespacedList.add(cachedPath); this.containedPaths.add(cachedPath);
}); }
if(!cachedPath.getFileName().endsWith(".mcmeta"))
namespacedList.add(cachedPath);
});
}
packTypedMap.put(namespace, namespacedList.build());
} catch(IOException ignored) {
} }
packTypedMap.put(namespace, namespacedList.build());
} catch(IOException ignored) {
} }
} synchronized (this.resourceListings) {
this.resourceListings.put(type, packTypedMap.build()); this.resourceListings.put(type, packTypedMap.build());
}
}, ModernFix.resourceReloadExecutor());
} }
((ObjectOpenHashSet<CachedResourcePath>)this.containedPaths).trim(); future = future.thenRunAsync(() -> {
((ObjectOpenHashSet<CachedResourcePath>)this.containedPaths).trim();
watch.stop();
}, ModernFix.resourceReloadExecutor());
this.cacheFuture = future;
// print debug message in separate task to prevent slowing down rest of load
future.thenRunAsync(() -> {
ModernFix.LOGGER.debug("Generated cache for {} in {}", debugPath, watch);
}, ModernFix.resourceReloadExecutor());
} }
private static boolean isValidCachedResourcePath(Path path) { private static boolean isValidCachedResourcePath(Path path) {
@ -86,13 +108,22 @@ public class PackResourcesCacheEngine {
return null; return null;
} }
private void awaitLoad() {
if(this.cacheFuture != null) {
this.cacheFuture.join();
this.cacheFuture = null;
}
}
public boolean hasResource(String path) { public boolean hasResource(String path) {
awaitLoad();
return this.containedPaths.contains(new CachedResourcePath(path)); return this.containedPaths.contains(new CachedResourcePath(path));
} }
public Collection<ResourceLocation> getResources(PackType type, String resourceNamespace, String pathIn, int maxDepth, Predicate<String> filter) { public Collection<ResourceLocation> getResources(PackType type, String resourceNamespace, String pathIn, int maxDepth, Predicate<String> filter) {
if(!PackTypeHelper.isVanillaPackType(type)) if(!PackTypeHelper.isVanillaPackType(type))
throw new IllegalArgumentException("Only vanilla PackTypes are supported"); throw new IllegalArgumentException("Only vanilla PackTypes are supported");
awaitLoad();
List<CachedResourcePath> paths = resourceListings.get(type).getOrDefault(resourceNamespace, Collections.emptyList()); List<CachedResourcePath> paths = resourceListings.get(type).getOrDefault(resourceNamespace, Collections.emptyList());
if(paths.isEmpty()) if(paths.isEmpty())
return Collections.emptyList(); return Collections.emptyList();