package net.minecraft.server.packs.resources; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.mojang.logging.LogUtils; import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.TreeMap; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Stream; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.PackResources; import net.minecraft.server.packs.PackType; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; public class FallbackResourceManager implements ResourceManager { static final Logger LOGGER = LogUtils.getLogger(); protected final List fallbacks = Lists.newArrayList(); private final PackType type; private final String namespace; public FallbackResourceManager(PackType type, String namespace) { this.type = type; this.namespace = namespace; } public void push(PackResources resources) { this.pushInternal(resources.packId(), resources, null); } public void push(PackResources resources, Predicate filter) { this.pushInternal(resources.packId(), resources, filter); } public void pushFilterOnly(String name, Predicate filter) { this.pushInternal(name, null, filter); } private void pushInternal(String name, @Nullable PackResources resources, @Nullable Predicate filter) { this.fallbacks.add(new FallbackResourceManager.PackEntry(name, resources, filter)); } @Override public Set getNamespaces() { return ImmutableSet.of(this.namespace); } @Override public Optional getResource(ResourceLocation resourceLocation) { for (int i = this.fallbacks.size() - 1; i >= 0; i--) { FallbackResourceManager.PackEntry packEntry = (FallbackResourceManager.PackEntry)this.fallbacks.get(i); PackResources packResources = packEntry.resources; if (packResources != null) { IoSupplier ioSupplier = packResources.getResource(this.type, resourceLocation); if (ioSupplier != null) { IoSupplier ioSupplier2 = this.createStackMetadataFinder(resourceLocation, i); return Optional.of(createResource(packResources, resourceLocation, ioSupplier, ioSupplier2)); } } if (packEntry.isFiltered(resourceLocation)) { LOGGER.warn("Resource {} not found, but was filtered by pack {}", resourceLocation, packEntry.name); return Optional.empty(); } } return Optional.empty(); } private static Resource createResource( PackResources source, ResourceLocation location, IoSupplier streamSupplier, IoSupplier metadataSupplier ) { return new Resource(source, wrapForDebug(location, source, streamSupplier), metadataSupplier); } private static IoSupplier wrapForDebug(ResourceLocation location, PackResources packResources, IoSupplier stream) { return LOGGER.isDebugEnabled() ? () -> new FallbackResourceManager.LeakedResourceWarningInputStream(stream.get(), location, packResources.packId()) : stream; } @Override public List getResourceStack(ResourceLocation location) { ResourceLocation resourceLocation = getMetadataLocation(location); List list = new ArrayList(); boolean bl = false; String string = null; for (int i = this.fallbacks.size() - 1; i >= 0; i--) { FallbackResourceManager.PackEntry packEntry = (FallbackResourceManager.PackEntry)this.fallbacks.get(i); PackResources packResources = packEntry.resources; if (packResources != null) { IoSupplier ioSupplier = packResources.getResource(this.type, location); if (ioSupplier != null) { IoSupplier ioSupplier2; if (bl) { ioSupplier2 = ResourceMetadata.EMPTY_SUPPLIER; } else { ioSupplier2 = () -> { IoSupplier ioSupplierx = packResources.getResource(this.type, resourceLocation); return ioSupplierx != null ? parseMetadata(ioSupplierx) : ResourceMetadata.EMPTY; }; } list.add(new Resource(packResources, ioSupplier, ioSupplier2)); } } if (packEntry.isFiltered(location)) { string = packEntry.name; break; } if (packEntry.isFiltered(resourceLocation)) { bl = true; } } if (list.isEmpty() && string != null) { LOGGER.warn("Resource {} not found, but was filtered by pack {}", location, string); } return Lists.reverse(list); } private static boolean isMetadata(ResourceLocation location) { return location.getPath().endsWith(".mcmeta"); } private static ResourceLocation getResourceLocationFromMetadata(ResourceLocation metadataResourceLocation) { String string = metadataResourceLocation.getPath().substring(0, metadataResourceLocation.getPath().length() - ".mcmeta".length()); return metadataResourceLocation.withPath(string); } static ResourceLocation getMetadataLocation(ResourceLocation location) { return location.withPath(location.getPath() + ".mcmeta"); } @Override public Map listResources(String path, Predicate filter) { record ResourceWithSourceAndIndex(PackResources packResources, IoSupplier resource, int packIndex) { } Map map = new HashMap(); Map map2 = new HashMap(); int i = this.fallbacks.size(); for (int j = 0; j < i; j++) { FallbackResourceManager.PackEntry packEntry = (FallbackResourceManager.PackEntry)this.fallbacks.get(j); packEntry.filterAll(map.keySet()); packEntry.filterAll(map2.keySet()); PackResources packResources = packEntry.resources; if (packResources != null) { int k = j; packResources.listResources(this.type, this.namespace, path, (resourceLocation, ioSupplier) -> { if (isMetadata(resourceLocation)) { if (filter.test(getResourceLocationFromMetadata(resourceLocation))) { map2.put(resourceLocation, new ResourceWithSourceAndIndex(packResources, ioSupplier, k)); } } else if (filter.test(resourceLocation)) { map.put(resourceLocation, new ResourceWithSourceAndIndex(packResources, ioSupplier, k)); } }); } } Map map3 = Maps.newTreeMap(); map.forEach((resourceLocation, arg) -> { ResourceLocation resourceLocation2 = getMetadataLocation(resourceLocation); ResourceWithSourceAndIndex lv = (ResourceWithSourceAndIndex)map2.get(resourceLocation2); IoSupplier ioSupplier; if (lv != null && lv.packIndex >= arg.packIndex) { ioSupplier = convertToMetadata(lv.resource); } else { ioSupplier = ResourceMetadata.EMPTY_SUPPLIER; } map3.put(resourceLocation, createResource(arg.packResources, resourceLocation, arg.resource, ioSupplier)); }); return map3; } private IoSupplier createStackMetadataFinder(ResourceLocation location, int fallbackIndex) { return () -> { ResourceLocation resourceLocation2 = getMetadataLocation(location); for (int j = this.fallbacks.size() - 1; j >= fallbackIndex; j--) { FallbackResourceManager.PackEntry packEntry = (FallbackResourceManager.PackEntry)this.fallbacks.get(j); PackResources packResources = packEntry.resources; if (packResources != null) { IoSupplier ioSupplier = packResources.getResource(this.type, resourceLocation2); if (ioSupplier != null) { return parseMetadata(ioSupplier); } } if (packEntry.isFiltered(resourceLocation2)) { break; } } return ResourceMetadata.EMPTY; }; } private static IoSupplier convertToMetadata(IoSupplier streamSupplier) { return () -> parseMetadata(streamSupplier); } private static ResourceMetadata parseMetadata(IoSupplier streamSupplier) throws IOException { InputStream inputStream = streamSupplier.get(); ResourceMetadata var2; try { var2 = ResourceMetadata.fromJsonStream(inputStream); } catch (Throwable var5) { if (inputStream != null) { try { inputStream.close(); } catch (Throwable var4) { var5.addSuppressed(var4); } } throw var5; } if (inputStream != null) { inputStream.close(); } return var2; } private static void applyPackFiltersToExistingResources( FallbackResourceManager.PackEntry packEntry, Map resources ) { for (FallbackResourceManager.EntryStack entryStack : resources.values()) { if (packEntry.isFiltered(entryStack.fileLocation)) { entryStack.fileSources.clear(); } else if (packEntry.isFiltered(entryStack.metadataLocation())) { entryStack.metaSources.clear(); } } } private void listPackResources( FallbackResourceManager.PackEntry entry, String path, Predicate filter, Map output ) { PackResources packResources = entry.resources; if (packResources != null) { packResources.listResources( this.type, this.namespace, path, (resourceLocation, ioSupplier) -> { if (isMetadata(resourceLocation)) { ResourceLocation resourceLocation2 = getResourceLocationFromMetadata(resourceLocation); if (!filter.test(resourceLocation2)) { return; } ((FallbackResourceManager.EntryStack)output.computeIfAbsent(resourceLocation2, FallbackResourceManager.EntryStack::new)) .metaSources .put(packResources, ioSupplier); } else { if (!filter.test(resourceLocation)) { return; } ((FallbackResourceManager.EntryStack)output.computeIfAbsent(resourceLocation, FallbackResourceManager.EntryStack::new)) .fileSources .add(new FallbackResourceManager.ResourceWithSource(packResources, ioSupplier)); } } ); } } @Override public Map> listResourceStacks(String path, Predicate filter) { Map map = Maps.newHashMap(); for (FallbackResourceManager.PackEntry packEntry : this.fallbacks) { applyPackFiltersToExistingResources(packEntry, map); this.listPackResources(packEntry, path, filter, map); } TreeMap> treeMap = Maps.newTreeMap(); for (FallbackResourceManager.EntryStack entryStack : map.values()) { if (!entryStack.fileSources.isEmpty()) { List list = new ArrayList(); for (FallbackResourceManager.ResourceWithSource resourceWithSource : entryStack.fileSources) { PackResources packResources = resourceWithSource.source; IoSupplier ioSupplier = (IoSupplier)entryStack.metaSources.get(packResources); IoSupplier ioSupplier2 = ioSupplier != null ? convertToMetadata(ioSupplier) : ResourceMetadata.EMPTY_SUPPLIER; list.add(createResource(packResources, entryStack.fileLocation, resourceWithSource.resource, ioSupplier2)); } treeMap.put(entryStack.fileLocation, list); } } return treeMap; } @Override public Stream listPacks() { return this.fallbacks.stream().map(packEntry -> packEntry.resources).filter(Objects::nonNull); } record EntryStack( ResourceLocation fileLocation, ResourceLocation metadataLocation, List fileSources, Map> metaSources ) { EntryStack(ResourceLocation fileLocation) { this(fileLocation, FallbackResourceManager.getMetadataLocation(fileLocation), new ArrayList(), new Object2ObjectArrayMap<>()); } } static class LeakedResourceWarningInputStream extends FilterInputStream { private final Supplier message; private boolean closed; public LeakedResourceWarningInputStream(InputStream inputStream, ResourceLocation resourceLocation, String packName) { super(inputStream); Exception exception = new Exception("Stacktrace"); this.message = () -> { StringWriter stringWriter = new StringWriter(); exception.printStackTrace(new PrintWriter(stringWriter)); return "Leaked resource: '" + resourceLocation + "' loaded from pack: '" + packName + "'\n" + stringWriter; }; } public void close() throws IOException { super.close(); this.closed = true; } protected void finalize() throws Throwable { if (!this.closed) { FallbackResourceManager.LOGGER.warn("{}", this.message.get()); } super.finalize(); } } record PackEntry(String name, @Nullable PackResources resources, @Nullable Predicate filter) { public void filterAll(Collection locations) { if (this.filter != null) { locations.removeIf(this.filter); } } public boolean isFiltered(ResourceLocation location) { return this.filter != null && this.filter.test(location); } } record ResourceWithSource(PackResources source, IoSupplier resource) { } }