diff --git a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ModNioResourcePack.java b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ModNioResourcePack.java
index 470b4e9f4..992783ff5 100644
--- a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ModNioResourcePack.java
+++ b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ModNioResourcePack.java
@@ -20,13 +20,18 @@ import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.file.DirectoryStream;
+import java.nio.file.FileVisitResult;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.EnumMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.function.Predicate;
 import java.util.regex.Pattern;
@@ -41,38 +46,136 @@ import net.minecraft.util.InvalidIdentifierException;
 
 import net.fabricmc.fabric.api.resource.ModResourcePack;
 import net.fabricmc.fabric.api.resource.ResourcePackActivationType;
+import net.fabricmc.loader.api.ModContainer;
 import net.fabricmc.loader.api.metadata.ModMetadata;
 
 public class ModNioResourcePack extends AbstractFileResourcePack implements ModResourcePack {
 	private static final Logger LOGGER = LoggerFactory.getLogger(ModNioResourcePack.class);
 	private static final Pattern RESOURCE_PACK_PATH = Pattern.compile("[a-z0-9-_]+");
-	private final ModMetadata modInfo;
-	private final Path basePath;
-	private final ResourceType type;
-	private final boolean cacheable;
-	private final AutoCloseable closer;
-	private final String separator;
-	private final ResourcePackActivationType activationType;
 
-	public ModNioResourcePack(ModMetadata modInfo, Path path, ResourceType type, AutoCloseable closer, ResourcePackActivationType activationType) {
+	private final String name;
+	private final ModMetadata modInfo;
+	private final List<Path> basePaths;
+	private final ResourceType type;
+	private final AutoCloseable closer;
+	private final ResourcePackActivationType activationType;
+	private final Map<ResourceType, Set<String>> namespaces;
+
+	public static ModNioResourcePack create(String name, ModContainer mod, String subPath, ResourceType type, ResourcePackActivationType activationType) {
+		List<Path> rootPaths = mod.getRootPaths();
+		List<Path> paths;
+
+		if (subPath == null) {
+			paths = rootPaths;
+		} else {
+			paths = new ArrayList<>(rootPaths.size());
+
+			for (Path path : rootPaths) {
+				path = path.toAbsolutePath().normalize();
+				Path childPath = path.resolve(subPath.replace("/", path.getFileSystem().getSeparator())).normalize();
+
+				if (!childPath.startsWith(path) || !Files.exists(childPath)) {
+					continue;
+				}
+
+				paths.add(childPath);
+			}
+		}
+
+		if (paths.isEmpty()) return null;
+
+		ModNioResourcePack ret = new ModNioResourcePack(name, mod.getMetadata(), paths, type, null, activationType);
+
+		return ret.getNamespaces(type).isEmpty() ? null : ret;
+	}
+
+	private ModNioResourcePack(String name, ModMetadata modInfo, List<Path> paths, ResourceType type, AutoCloseable closer, ResourcePackActivationType activationType) {
 		super(null);
+
+		this.name = name;
 		this.modInfo = modInfo;
-		this.basePath = path.toAbsolutePath().normalize();
+		this.basePaths = paths;
 		this.type = type;
-		this.cacheable = false; /* TODO */
 		this.closer = closer;
-		this.separator = basePath.getFileSystem().getSeparator();
 		this.activationType = activationType;
+		this.namespaces = readNamespaces(paths, modInfo.getId());
+	}
+
+	private static Map<ResourceType, Set<String>> readNamespaces(List<Path> paths, String modId) {
+		Map<ResourceType, Set<String>> ret = new EnumMap<>(ResourceType.class);
+
+		for (ResourceType type : ResourceType.values()) {
+			Set<String> namespaces = null;
+
+			for (Path path : paths) {
+				Path dir = path.resolve(type.getDirectory());
+				if (!Files.isDirectory(dir)) continue;
+
+				String separator = path.getFileSystem().getSeparator();
+
+				try (DirectoryStream<Path> ds = Files.newDirectoryStream(dir)) {
+					for (Path p : ds) {
+						if (!Files.isDirectory(p)) continue;
+
+						String s = p.getFileName().toString();
+						// s may contain trailing slashes, remove them
+						s = s.replace(separator, "");
+
+						if (!RESOURCE_PACK_PATH.matcher(s).matches()) {
+							LOGGER.warn("Fabric NioResourcePack: ignored invalid namespace: {} in mod ID {}", s, modId);
+							continue;
+						}
+
+						if (namespaces == null) namespaces = new HashSet<>();
+
+						namespaces.add(s);
+					}
+				} catch (IOException e) {
+					LOGGER.warn("getNamespaces in mod " + modId + " failed!", e);
+				}
+			}
+
+			ret.put(type, namespaces != null ? namespaces : Collections.emptySet());
+		}
+
+		return ret;
 	}
 
 	private Path getPath(String filename) {
-		Path childPath = basePath.resolve(filename.replace("/", separator)).toAbsolutePath().normalize();
+		if (hasAbsentNs(filename)) return null;
 
-		if (childPath.startsWith(basePath) && Files.exists(childPath)) {
-			return childPath;
-		} else {
-			return null;
+		for (Path basePath : basePaths) {
+			Path childPath = basePath.resolve(filename.replace("/", basePath.getFileSystem().getSeparator())).toAbsolutePath().normalize();
+
+			if (childPath.startsWith(basePath) && Files.exists(childPath)) {
+				return childPath;
+			}
 		}
+
+		return null;
+	}
+
+	private static final String resPrefix = ResourceType.CLIENT_RESOURCES.getDirectory()+"/";
+	private static final String dataPrefix = ResourceType.SERVER_DATA.getDirectory()+"/";
+
+	private boolean hasAbsentNs(String filename) {
+		int prefixLen;
+		ResourceType type;
+
+		if (filename.startsWith(resPrefix)) {
+			prefixLen = resPrefix.length();
+			type = ResourceType.CLIENT_RESOURCES;
+		} else if (filename.startsWith(dataPrefix)) {
+			prefixLen = dataPrefix.length();
+			type = ResourceType.SERVER_DATA;
+		} else {
+			return false;
+		}
+
+		int nsEnd = filename.indexOf('/', prefixLen);
+		if (nsEnd < 0) return false;
+
+		return !namespaces.get(type).contains(filename.substring(prefixLen, nsEnd));
 	}
 
 	@Override
@@ -107,84 +210,47 @@ public class ModNioResourcePack extends AbstractFileResourcePack implements ModR
 
 	@Override
 	public Collection<Identifier> findResources(ResourceType type, String namespace, String path, int depth, Predicate<String> predicate) {
+		if (!namespaces.getOrDefault(type, Collections.emptySet()).contains(namespace)) {
+			return Collections.emptyList();
+		}
+
 		List<Identifier> ids = new ArrayList<>();
-		String nioPath = path.replace("/", separator);
 
-		Path namespacePath = getPath(type.getDirectory() + "/" + namespace);
+		for (Path basePath : basePaths) {
+			String separator = basePath.getFileSystem().getSeparator();
+			Path nsPath = basePath.resolve(type.getDirectory()).resolve(namespace);
+			Path searchPath = nsPath.resolve(path.replace("/", separator)).normalize();
+			if (!Files.exists(searchPath)) continue;
 
-		if (namespacePath != null) {
-			Path searchPath = namespacePath.resolve(nioPath).toAbsolutePath().normalize();
+			try {
+				Files.walkFileTree(searchPath, new SimpleFileVisitor<Path>() {
+					@Override
+					public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+						String fileName = file.getFileName().toString();
 
-			if (Files.exists(searchPath)) {
-				try {
-					Files.walk(searchPath, depth)
-							.filter(Files::isRegularFile)
-							.filter((p) -> {
-								String filename = p.getFileName().toString();
-								return !filename.endsWith(".mcmeta") && predicate.test(filename);
-							})
-							.map(namespacePath::relativize)
-							.map((p) -> p.toString().replace(separator, "/"))
-							.forEach((s) -> {
-								try {
-									ids.add(new Identifier(namespace, s));
-								} catch (InvalidIdentifierException e) {
-									LOGGER.error(e.getMessage());
-								}
-							});
-				} catch (IOException e) {
-					LOGGER.warn("findResources at " + path + " in namespace " + namespace + ", mod " + modInfo.getId() + " failed!", e);
-				}
+						if (!fileName.endsWith(".mcmeta")
+								&& predicate.test(fileName)) {
+							try {
+								ids.add(new Identifier(namespace, nsPath.relativize(file).toString().replace(separator, "/")));
+							} catch (InvalidIdentifierException e) {
+								LOGGER.error(e.getMessage());
+							}
+						}
+
+						return FileVisitResult.CONTINUE;
+					}
+				});
+			} catch (IOException e) {
+				LOGGER.warn("findResources at " + path + " in namespace " + namespace + ", mod " + modInfo.getId() + " failed!", e);
 			}
 		}
 
 		return ids;
 	}
 
-	private Set<String> namespaceCache;
-
-	protected void warnInvalidNamespace(String s) {
-		LOGGER.warn("Fabric NioResourcePack: ignored invalid namespace: {} in mod ID {}", s, modInfo.getId());
-	}
-
 	@Override
 	public Set<String> getNamespaces(ResourceType type) {
-		if (namespaceCache != null) {
-			return namespaceCache;
-		}
-
-		try {
-			Path typePath = getPath(type.getDirectory());
-
-			if (typePath == null || !(Files.isDirectory(typePath))) {
-				return Collections.emptySet();
-			}
-
-			Set<String> namespaces = new HashSet<>();
-
-			try (DirectoryStream<Path> stream = Files.newDirectoryStream(typePath, Files::isDirectory)) {
-				for (Path path : stream) {
-					String s = path.getFileName().toString();
-					// s may contain trailing slashes, remove them
-					s = s.replace(separator, "");
-
-					if (RESOURCE_PACK_PATH.matcher(s).matches()) {
-						namespaces.add(s);
-					} else {
-						this.warnInvalidNamespace(s);
-					}
-				}
-			}
-
-			if (cacheable) {
-				namespaceCache = namespaces;
-			}
-
-			return namespaces;
-		} catch (IOException e) {
-			LOGGER.warn("getNamespaces in mod " + modInfo.getId() + " failed!", e);
-			return Collections.emptySet();
-		}
+		return namespaces.getOrDefault(type, Collections.emptySet());
 	}
 
 	@Override
@@ -209,6 +275,6 @@ public class ModNioResourcePack extends AbstractFileResourcePack implements ModR
 
 	@Override
 	public String getName() {
-		return ModResourcePackUtil.getName(modInfo);
+		return name;
 	}
 }
diff --git a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ModResourcePackUtil.java b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ModResourcePackUtil.java
index 8682fb808..667853335 100644
--- a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ModResourcePackUtil.java
+++ b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ModResourcePackUtil.java
@@ -17,8 +17,6 @@
 package net.fabricmc.fabric.impl.resource.loader;
 
 import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
 import java.util.List;
 
 import com.google.common.base.Charsets;
@@ -54,21 +52,9 @@ public final class ModResourcePackUtil {
 				continue;
 			}
 
-			Path path = container.getRootPath();
+			ModResourcePack pack = ModNioResourcePack.create(getName(container.getMetadata()), container, null, type, ResourcePackActivationType.ALWAYS_ENABLED);
 
-			if (subPath != null) {
-				Path childPath = path.resolve(subPath.replace("/", path.getFileSystem().getSeparator())).toAbsolutePath().normalize();
-
-				if (!childPath.startsWith(path) || !Files.exists(childPath)) {
-					continue;
-				}
-
-				path = childPath;
-			}
-
-			ModResourcePack pack = new ModNioResourcePack(container.getMetadata(), path, type, null, ResourcePackActivationType.ALWAYS_ENABLED);
-
-			if (!pack.getNamespaces(type).isEmpty()) {
+			if (pack != null) {
 				packs.add(pack);
 			}
 		}
diff --git a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ResourceManagerHelperImpl.java b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ResourceManagerHelperImpl.java
index 5aa7736a5..27618a3eb 100644
--- a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ResourceManagerHelperImpl.java
+++ b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ResourceManagerHelperImpl.java
@@ -69,30 +69,18 @@ public class ResourceManagerHelperImpl implements ResourceManagerHelper {
 	 * @see ResourceManagerHelper#registerBuiltinResourcePack(Identifier, String, ModContainer, boolean)
 	 */
 	public static boolean registerBuiltinResourcePack(Identifier id, String subPath, ModContainer container, ResourcePackActivationType activationType) {
-		String separator = container.getRootPath().getFileSystem().getSeparator();
-		subPath = subPath.replace("/", separator);
+		String name = id.getNamespace() + "/" + id.getPath();
+		ModNioResourcePack resourcePack = ModNioResourcePack.create(name, container, subPath, ResourceType.CLIENT_RESOURCES, activationType);
+		ModNioResourcePack dataPack = ModNioResourcePack.create(name, container, subPath, ResourceType.SERVER_DATA, activationType);
+		if (resourcePack == null && dataPack == null) return false;
 
-		Path resourcePackPath = container.getRootPath().resolve(subPath).toAbsolutePath().normalize();
-
-		if (!Files.exists(resourcePackPath)) {
-			return false;
+		if (resourcePack != null) {
+			builtinResourcePacks.add(new Pair<>(name, resourcePack));
 		}
 
-		String name = id.getNamespace() + "/" + id.getPath();
-
-		builtinResourcePacks.add(new Pair<>(name, new ModNioResourcePack(container.getMetadata(), resourcePackPath, ResourceType.CLIENT_RESOURCES, null, activationType) {
-			@Override
-			public String getName() {
-				return name; // Built-in resource pack provided by a mod, the name is overriden.
-			}
-		}));
-
-		builtinResourcePacks.add(new Pair<>(name, new ModNioResourcePack(container.getMetadata(), resourcePackPath, ResourceType.SERVER_DATA, null, activationType) {
-			@Override
-			public String getName() {
-				return name; // Built-in resource pack provided by a mod, the name is overriden.
-			}
-		}));
+		if (dataPack != null) {
+			builtinResourcePacks.add(new Pair<>(name, dataPack));
+		}
 
 		return true;
 	}
diff --git a/fabric-resource-loader-v0/src/main/resources/fabric.mod.json b/fabric-resource-loader-v0/src/main/resources/fabric.mod.json
index 60c01dfbe..7aa70d715 100644
--- a/fabric-resource-loader-v0/src/main/resources/fabric.mod.json
+++ b/fabric-resource-loader-v0/src/main/resources/fabric.mod.json
@@ -16,7 +16,7 @@
     "FabricMC"
   ],
   "depends": {
-    "fabricloader": ">=0.4.0"
+    "fabricloader": ">=0.13.0"
   },
   "description": "Asset and data resource loading.",
   "mixins": [