2025-11-24 22:52:51 +03:00

324 lines
15 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* com.google.common.collect.ImmutableSet
* com.google.common.collect.Lists
* com.google.common.collect.Maps
* com.mojang.logging.LogUtils
* it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
* org.jspecify.annotations.Nullable
* org.slf4j.Logger
*/
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.lang.invoke.LambdaMetafactory;
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.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
import net.minecraft.resources.Identifier;
import net.minecraft.server.packs.PackResources;
import net.minecraft.server.packs.PackType;
import net.minecraft.server.packs.resources.IoSupplier;
import net.minecraft.server.packs.resources.Resource;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.ResourceMetadata;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
public class FallbackResourceManager
implements ResourceManager {
private static final Logger LOGGER = LogUtils.getLogger();
protected final List<PackEntry> 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 pack) {
this.pushInternal(pack.packId(), pack, null);
}
public void push(PackResources pack, Predicate<Identifier> filter) {
this.pushInternal(pack.packId(), pack, filter);
}
public void pushFilterOnly(String name, Predicate<Identifier> filter) {
this.pushInternal(name, null, filter);
}
private void pushInternal(String name, @Nullable PackResources pack, @Nullable Predicate<Identifier> contentFilter) {
this.fallbacks.add(new PackEntry(name, pack, contentFilter));
}
@Override
public Set<String> getNamespaces() {
return ImmutableSet.of((Object)this.namespace);
}
@Override
public Optional<Resource> getResource(Identifier location) {
for (int i = this.fallbacks.size() - 1; i >= 0; --i) {
IoSupplier<InputStream> resource;
PackEntry entry = this.fallbacks.get(i);
PackResources fallback = entry.resources;
if (fallback != null && (resource = fallback.getResource(this.type, location)) != null) {
IoSupplier<ResourceMetadata> metadataGetter = this.createStackMetadataFinder(location, i);
return Optional.of(FallbackResourceManager.createResource(fallback, location, resource, metadataGetter));
}
if (!entry.isFiltered(location)) continue;
LOGGER.warn("Resource {} not found, but was filtered by pack {}", (Object)location, (Object)entry.name);
return Optional.empty();
}
return Optional.empty();
}
private static Resource createResource(PackResources source, Identifier location, IoSupplier<InputStream> resource, IoSupplier<ResourceMetadata> metadata) {
return new Resource(source, FallbackResourceManager.wrapForDebug(location, source, resource), metadata);
}
private static IoSupplier<InputStream> wrapForDebug(Identifier location, PackResources source, IoSupplier<InputStream> resource) {
if (LOGGER.isDebugEnabled()) {
return () -> new LeakedResourceWarningInputStream((InputStream)resource.get(), location, source.packId());
}
return resource;
}
@Override
public List<Resource> getResourceStack(Identifier location) {
Identifier metadataLocation = FallbackResourceManager.getMetadataLocation(location);
ArrayList<Resource> result = new ArrayList<Resource>();
boolean filterMeta = false;
String lastFilterName = null;
for (int i = this.fallbacks.size() - 1; i >= 0; --i) {
IoSupplier<InputStream> resource;
PackEntry entry = this.fallbacks.get(i);
PackResources fileSource = entry.resources;
if (fileSource != null && (resource = fileSource.getResource(this.type, location)) != null) {
IoSupplier<ResourceMetadata> metadataGetter = filterMeta ? ResourceMetadata.EMPTY_SUPPLIER : () -> {
IoSupplier<InputStream> metaResource = fileSource.getResource(this.type, metadataLocation);
return metaResource != null ? FallbackResourceManager.parseMetadata(metaResource) : ResourceMetadata.EMPTY;
};
result.add(new Resource(fileSource, resource, metadataGetter));
}
if (entry.isFiltered(location)) {
lastFilterName = entry.name;
break;
}
if (!entry.isFiltered(metadataLocation)) continue;
filterMeta = true;
}
if (result.isEmpty() && lastFilterName != null) {
LOGGER.warn("Resource {} not found, but was filtered by pack {}", (Object)location, lastFilterName);
}
return Lists.reverse(result);
}
private static boolean isMetadata(Identifier location) {
return location.getPath().endsWith(".mcmeta");
}
private static Identifier getIdentifierFromMetadata(Identifier identifier) {
String newPath = identifier.getPath().substring(0, identifier.getPath().length() - ".mcmeta".length());
return identifier.withPath(newPath);
}
private static Identifier getMetadataLocation(Identifier identifier) {
return identifier.withPath(identifier.getPath() + ".mcmeta");
}
@Override
public Map<Identifier, Resource> listResources(String directory, Predicate<Identifier> filter) {
record ResourceWithSourceAndIndex(PackResources packResources, IoSupplier<InputStream> resource, int packIndex) {
}
HashMap<Identifier, ResourceWithSourceAndIndex> topResourceForFileLocation = new HashMap<Identifier, ResourceWithSourceAndIndex>();
HashMap topResourceForMetaLocation = new HashMap();
int packCount = this.fallbacks.size();
for (int i = 0; i < packCount; ++i) {
PackEntry entry = this.fallbacks.get(i);
entry.filterAll(topResourceForFileLocation.keySet());
entry.filterAll(topResourceForMetaLocation.keySet());
PackResources packResources = entry.resources;
if (packResources == null) continue;
int packIndex = i;
packResources.listResources(this.type, this.namespace, directory, (resource, streamSupplier) -> {
if (FallbackResourceManager.isMetadata(resource)) {
if (filter.test(FallbackResourceManager.getIdentifierFromMetadata(resource))) {
topResourceForMetaLocation.put(resource, new ResourceWithSourceAndIndex(packResources, (IoSupplier<InputStream>)streamSupplier, packIndex));
}
} else if (filter.test((Identifier)resource)) {
topResourceForFileLocation.put((Identifier)resource, new ResourceWithSourceAndIndex(packResources, (IoSupplier<InputStream>)streamSupplier, packIndex));
}
});
}
TreeMap result = Maps.newTreeMap();
topResourceForFileLocation.forEach((location, resource) -> {
Identifier metadataLocation = FallbackResourceManager.getMetadataLocation(location);
ResourceWithSourceAndIndex metaResource = (ResourceWithSourceAndIndex)topResourceForMetaLocation.get(metadataLocation);
IoSupplier<ResourceMetadata> metaGetter = metaResource != null && metaResource.packIndex >= resource.packIndex ? FallbackResourceManager.convertToMetadata(metaResource.resource) : ResourceMetadata.EMPTY_SUPPLIER;
result.put(location, FallbackResourceManager.createResource(resource.packResources, location, resource.resource, metaGetter));
});
return result;
}
private IoSupplier<ResourceMetadata> createStackMetadataFinder(Identifier location, int finalPackIndex) {
return () -> {
Identifier metadataLocation = FallbackResourceManager.getMetadataLocation(location);
for (int i = this.fallbacks.size() - 1; i >= finalPackIndex; --i) {
IoSupplier<InputStream> resource;
PackEntry entry = this.fallbacks.get(i);
PackResources metadataPackCandidate = entry.resources;
if (metadataPackCandidate != null && (resource = metadataPackCandidate.getResource(this.type, metadataLocation)) != null) {
return FallbackResourceManager.parseMetadata(resource);
}
if (entry.isFiltered(metadataLocation)) break;
}
return ResourceMetadata.EMPTY;
};
}
private static IoSupplier<ResourceMetadata> convertToMetadata(IoSupplier<InputStream> input) {
return () -> FallbackResourceManager.parseMetadata(input);
}
private static ResourceMetadata parseMetadata(IoSupplier<InputStream> input) throws IOException {
try (InputStream metadata = input.get();){
ResourceMetadata resourceMetadata = ResourceMetadata.fromJsonStream(metadata);
return resourceMetadata;
}
}
private static void applyPackFiltersToExistingResources(PackEntry entry, Map<Identifier, EntryStack> foundResources) {
for (EntryStack e : foundResources.values()) {
if (entry.isFiltered(e.fileLocation)) {
e.fileSources.clear();
continue;
}
if (!entry.isFiltered(e.metadataLocation())) continue;
e.metaSources.clear();
}
}
private void listPackResources(PackEntry entry, String directory, Predicate<Identifier> filter, Map<Identifier, EntryStack> foundResources) {
PackResources pack = entry.resources;
if (pack == null) {
return;
}
pack.listResources(this.type, this.namespace, directory, (id, resource) -> {
if (FallbackResourceManager.isMetadata(id)) {
Identifier actualId = FallbackResourceManager.getIdentifierFromMetadata(id);
if (!filter.test(actualId)) {
return;
}
foundResources.computeIfAbsent(actualId, (Function<Identifier, EntryStack>)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)Ljava/lang/Object;, <init>(net.minecraft.resources.Identifier ), (Lnet/minecraft/resources/Identifier;)Lnet/minecraft/server/packs/resources/FallbackResourceManager$EntryStack;)()).metaSources.put(pack, (IoSupplier<InputStream>)resource);
} else {
if (!filter.test((Identifier)id)) {
return;
}
foundResources.computeIfAbsent(id, (Function<Identifier, EntryStack>)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)Ljava/lang/Object;, <init>(net.minecraft.resources.Identifier ), (Lnet/minecraft/resources/Identifier;)Lnet/minecraft/server/packs/resources/FallbackResourceManager$EntryStack;)()).fileSources.add(new ResourceWithSource(pack, (IoSupplier<InputStream>)resource));
}
});
}
@Override
public Map<Identifier, List<Resource>> listResourceStacks(String directory, Predicate<Identifier> filter) {
HashMap foundResources = Maps.newHashMap();
for (PackEntry entry : this.fallbacks) {
FallbackResourceManager.applyPackFiltersToExistingResources(entry, foundResources);
this.listPackResources(entry, directory, filter, foundResources);
}
TreeMap result = Maps.newTreeMap();
for (EntryStack entry : foundResources.values()) {
if (entry.fileSources.isEmpty()) continue;
ArrayList<Resource> resources = new ArrayList<Resource>();
for (ResourceWithSource stackEntry : entry.fileSources) {
PackResources source = stackEntry.source;
IoSupplier<InputStream> metaSource = entry.metaSources.get(source);
IoSupplier<ResourceMetadata> metaGetter = metaSource != null ? FallbackResourceManager.convertToMetadata(metaSource) : ResourceMetadata.EMPTY_SUPPLIER;
resources.add(FallbackResourceManager.createResource(source, entry.fileLocation, stackEntry.resource, metaGetter));
}
result.put(entry.fileLocation, resources);
}
return result;
}
@Override
public Stream<PackResources> listPacks() {
return this.fallbacks.stream().map(p -> p.resources).filter(Objects::nonNull);
}
private record PackEntry(String name, @Nullable PackResources resources, @Nullable Predicate<Identifier> filter) {
public void filterAll(Collection<Identifier> collection) {
if (this.filter != null) {
collection.removeIf(this.filter);
}
}
public boolean isFiltered(Identifier location) {
return this.filter != null && this.filter.test(location);
}
}
private record EntryStack(Identifier fileLocation, Identifier metadataLocation, List<ResourceWithSource> fileSources, Map<PackResources, IoSupplier<InputStream>> metaSources) {
EntryStack(Identifier fileLocation) {
this(fileLocation, FallbackResourceManager.getMetadataLocation(fileLocation), new ArrayList<ResourceWithSource>(), (Map<PackResources, IoSupplier<InputStream>>)new Object2ObjectArrayMap());
}
}
private record ResourceWithSource(PackResources source, IoSupplier<InputStream> resource) {
}
private static class LeakedResourceWarningInputStream
extends FilterInputStream {
private final Supplier<String> message;
private boolean closed;
public LeakedResourceWarningInputStream(InputStream wrapped, Identifier location, String name) {
super(wrapped);
Exception exception = new Exception("Stacktrace");
this.message = () -> {
StringWriter data = new StringWriter();
exception.printStackTrace(new PrintWriter(data));
return "Leaked resource: '" + String.valueOf(location) + "' loaded from pack: '" + name + "'\n" + String.valueOf(data);
};
}
@Override
public void close() throws IOException {
super.close();
this.closed = true;
}
protected void finalize() throws Throwable {
if (!this.closed) {
LOGGER.warn("{}", (Object)this.message.get());
}
super.finalize();
}
}
}