minecraft-src/net/minecraft/server/packs/resources/FallbackResourceManager.java
2025-07-04 01:41:11 +03:00

382 lines
13 KiB
Java

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<FallbackResourceManager.PackEntry> fallbacks = Lists.<FallbackResourceManager.PackEntry>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<ResourceLocation> filter) {
this.pushInternal(resources.packId(), resources, filter);
}
public void pushFilterOnly(String name, Predicate<ResourceLocation> filter) {
this.pushInternal(name, null, filter);
}
private void pushInternal(String name, @Nullable PackResources resources, @Nullable Predicate<ResourceLocation> filter) {
this.fallbacks.add(new FallbackResourceManager.PackEntry(name, resources, filter));
}
@Override
public Set<String> getNamespaces() {
return ImmutableSet.of(this.namespace);
}
@Override
public Optional<Resource> 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<InputStream> ioSupplier = packResources.getResource(this.type, resourceLocation);
if (ioSupplier != null) {
IoSupplier<ResourceMetadata> 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<InputStream> streamSupplier, IoSupplier<ResourceMetadata> metadataSupplier
) {
return new Resource(source, wrapForDebug(location, source, streamSupplier), metadataSupplier);
}
private static IoSupplier<InputStream> wrapForDebug(ResourceLocation location, PackResources packResources, IoSupplier<InputStream> stream) {
return LOGGER.isDebugEnabled() ? () -> new FallbackResourceManager.LeakedResourceWarningInputStream(stream.get(), location, packResources.packId()) : stream;
}
@Override
public List<Resource> getResourceStack(ResourceLocation location) {
ResourceLocation resourceLocation = getMetadataLocation(location);
List<Resource> 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<InputStream> ioSupplier = packResources.getResource(this.type, location);
if (ioSupplier != null) {
IoSupplier<ResourceMetadata> ioSupplier2;
if (bl) {
ioSupplier2 = ResourceMetadata.EMPTY_SUPPLIER;
} else {
ioSupplier2 = () -> {
IoSupplier<InputStream> 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<ResourceLocation, Resource> listResources(String path, Predicate<ResourceLocation> filter) {
record ResourceWithSourceAndIndex(PackResources packResources, IoSupplier<InputStream> resource, int packIndex) {
}
Map<ResourceLocation, ResourceWithSourceAndIndex> map = new HashMap();
Map<ResourceLocation, ResourceWithSourceAndIndex> 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<ResourceLocation, Resource> map3 = Maps.<ResourceLocation, Resource>newTreeMap();
map.forEach((resourceLocation, arg) -> {
ResourceLocation resourceLocation2 = getMetadataLocation(resourceLocation);
ResourceWithSourceAndIndex lv = (ResourceWithSourceAndIndex)map2.get(resourceLocation2);
IoSupplier<ResourceMetadata> 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<ResourceMetadata> 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<InputStream> ioSupplier = packResources.getResource(this.type, resourceLocation2);
if (ioSupplier != null) {
return parseMetadata(ioSupplier);
}
}
if (packEntry.isFiltered(resourceLocation2)) {
break;
}
}
return ResourceMetadata.EMPTY;
};
}
private static IoSupplier<ResourceMetadata> convertToMetadata(IoSupplier<InputStream> streamSupplier) {
return () -> parseMetadata(streamSupplier);
}
private static ResourceMetadata parseMetadata(IoSupplier<InputStream> 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<ResourceLocation, FallbackResourceManager.EntryStack> 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<ResourceLocation> filter, Map<ResourceLocation, FallbackResourceManager.EntryStack> 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<ResourceLocation, List<Resource>> listResourceStacks(String path, Predicate<ResourceLocation> filter) {
Map<ResourceLocation, FallbackResourceManager.EntryStack> map = Maps.<ResourceLocation, FallbackResourceManager.EntryStack>newHashMap();
for (FallbackResourceManager.PackEntry packEntry : this.fallbacks) {
applyPackFiltersToExistingResources(packEntry, map);
this.listPackResources(packEntry, path, filter, map);
}
TreeMap<ResourceLocation, List<Resource>> treeMap = Maps.newTreeMap();
for (FallbackResourceManager.EntryStack entryStack : map.values()) {
if (!entryStack.fileSources.isEmpty()) {
List<Resource> list = new ArrayList();
for (FallbackResourceManager.ResourceWithSource resourceWithSource : entryStack.fileSources) {
PackResources packResources = resourceWithSource.source;
IoSupplier<InputStream> ioSupplier = (IoSupplier<InputStream>)entryStack.metaSources.get(packResources);
IoSupplier<ResourceMetadata> 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<PackResources> listPacks() {
return this.fallbacks.stream().map(packEntry -> packEntry.resources).filter(Objects::nonNull);
}
record EntryStack(
ResourceLocation fileLocation,
ResourceLocation metadataLocation,
List<FallbackResourceManager.ResourceWithSource> fileSources,
Map<PackResources, IoSupplier<InputStream>> metaSources
) {
EntryStack(ResourceLocation fileLocation) {
this(fileLocation, FallbackResourceManager.getMetadataLocation(fileLocation), new ArrayList(), new Object2ObjectArrayMap<>());
}
}
static class LeakedResourceWarningInputStream extends FilterInputStream {
private final Supplier<String> 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<ResourceLocation> filter) {
public void filterAll(Collection<ResourceLocation> 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<InputStream> resource) {
}
}