382 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			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) {
 | |
| 	}
 | |
| }
 |