Add API to create reload listeners with a registry lookup. ()

* Add API to create reload listeners with a registry lookup.

* Document this is only for server data

* Review feedback
This commit is contained in:
modmuss 2024-07-14 15:01:57 +01:00 committed by GitHub
parent 8cfd8554f6
commit 7e687b320e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 127 additions and 7 deletions
fabric-resource-loader-v0/src
main
java/net/fabricmc/fabric
resources
testmod/java/net/fabricmc/fabric/test/resource/loader

View file

@ -16,8 +16,11 @@
package net.fabricmc.fabric.api.resource;
import java.util.function.Function;
import org.jetbrains.annotations.ApiStatus;
import net.minecraft.registry.RegistryWrapper;
import net.minecraft.resource.ResourceManager;
import net.minecraft.resource.ResourceType;
import net.minecraft.text.Text;
@ -49,6 +52,16 @@ public interface ResourceManagerHelper {
*/
void registerReloadListener(IdentifiableResourceReloadListener listener);
/**
* Register a resource reload listener for a given resource manager type.
*
* <p>Note: This is only supported for server data reload listeners.
*
* @param identifier The identifier of the listener.
* @param listenerFactory A function that creates a new instance of the listener with a given registry lookup.
*/
void registerReloadListener(Identifier identifier, Function<RegistryWrapper.WrapperLookup, IdentifiableResourceReloadListener> listenerFactory);
/**
* Get the ResourceManagerHelper instance for a given resource type.
*

View file

@ -25,13 +25,18 @@ import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import com.google.common.collect.Lists;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.minecraft.recipe.RecipeManager;
import net.minecraft.registry.RegistryWrapper;
import net.minecraft.resource.ResourcePack;
import net.minecraft.resource.ResourcePackInfo;
import net.minecraft.resource.ResourcePackPosition;
@ -53,10 +58,16 @@ public class ResourceManagerHelperImpl implements ResourceManagerHelper {
private static final Logger LOGGER = LoggerFactory.getLogger(ResourceManagerHelperImpl.class);
private final Set<Identifier> addedListenerIds = new HashSet<>();
private final Set<ListenerFactory> listenerFactories = new LinkedHashSet<>();
private final Set<IdentifiableResourceReloadListener> addedListeners = new LinkedHashSet<>();
private final ResourceType type;
private ResourceManagerHelperImpl(ResourceType type) {
this.type = type;
}
public static ResourceManagerHelperImpl get(ResourceType type) {
return registryMap.computeIfAbsent(type, (t) -> new ResourceManagerHelperImpl());
return registryMap.computeIfAbsent(type, ResourceManagerHelperImpl::new);
}
/**
@ -74,7 +85,7 @@ public class ResourceManagerHelperImpl implements ResourceManagerHelper {
public static boolean registerBuiltinResourcePack(Identifier id, String subPath, ModContainer container, Text displayName, ResourcePackActivationType activationType) {
// Assuming the mod has multiple paths, we simply "hope" that the file separator is *not* different across them
List<Path> paths = container.getRootPaths();
String separator = paths.get(0).getFileSystem().getSeparator();
String separator = paths.getFirst().getFileSystem().getSeparator();
subPath = subPath.replace("/", separator);
ModNioResourcePack resourcePack = ModNioResourcePack.create(id.toString(), container, subPath, ResourceType.CLIENT_RESOURCES, activationType, false);
ModNioResourcePack dataPack = ModNioResourcePack.create(id.toString(), container, subPath, ResourceType.SERVER_DATA, activationType, false);
@ -168,7 +179,16 @@ public class ResourceManagerHelperImpl implements ResourceManagerHelper {
// trust them 100%. Only code doesn't lie.
// - We addReloadListener all custom listeners after vanilla listeners. Same reasons.
List<IdentifiableResourceReloadListener> listenersToAdd = Lists.newArrayList(addedListeners);
final RegistryWrapper.WrapperLookup wrapperLookup = getWrapperLookup(listeners);
List<IdentifiableResourceReloadListener> listenersToAdd = Lists.newArrayList();
for (ListenerFactory addedListener : listenerFactories) {
listenersToAdd.add(addedListener.get(wrapperLookup));
}
addedListeners.clear();
addedListeners.addAll(listenersToAdd);
Set<Identifier> resolvedIds = new HashSet<>();
for (ResourceReloader listener : listeners) {
@ -200,15 +220,80 @@ public class ResourceManagerHelperImpl implements ResourceManagerHelper {
}
}
// A bit of a hack to get the registry, but it works.
@Nullable
private RegistryWrapper.WrapperLookup getWrapperLookup(List<ResourceReloader> listeners) {
if (type == ResourceType.CLIENT_RESOURCES) {
// We don't need the registry for client resources.
return null;
}
for (ResourceReloader resourceReloader : listeners) {
if (resourceReloader instanceof RecipeManager recipeManager) {
return recipeManager.registryLookup;
}
}
throw new IllegalStateException("No RecipeManager found in listeners!");
}
@Override
public void registerReloadListener(IdentifiableResourceReloadListener listener) {
if (!addedListenerIds.add(listener.getFabricId())) {
LOGGER.warn("Tried to register resource reload listener " + listener.getFabricId() + " twice!");
registerReloadListener(new SimpleResourceReloaderFactory(listener));
}
@Override
public void registerReloadListener(Identifier identifier, Function<RegistryWrapper.WrapperLookup, IdentifiableResourceReloadListener> listenerFactory) {
if (type == ResourceType.CLIENT_RESOURCES) {
throw new IllegalArgumentException("Cannot register a registry listener for the client resource type!");
}
registerReloadListener(new RegistryResourceReloaderFactory(identifier, listenerFactory));
}
private void registerReloadListener(ListenerFactory factory) {
if (!addedListenerIds.add(factory.id())) {
LOGGER.warn("Tried to register resource reload listener " + factory.id() + " twice!");
return;
}
if (!addedListeners.add(listener)) {
throw new RuntimeException("Listener with previously unknown ID " + listener.getFabricId() + " already in listener set!");
if (!listenerFactories.add(factory)) {
throw new RuntimeException("Listener with previously unknown ID " + factory.id() + " already in listener set!");
}
}
private sealed interface ListenerFactory permits SimpleResourceReloaderFactory, RegistryResourceReloaderFactory {
Identifier id();
IdentifiableResourceReloadListener get(RegistryWrapper.WrapperLookup registry);
}
private record SimpleResourceReloaderFactory(IdentifiableResourceReloadListener listener) implements ListenerFactory {
@Override
public Identifier id() {
return listener.getFabricId();
}
@Override
public IdentifiableResourceReloadListener get(RegistryWrapper.WrapperLookup registry) {
return listener;
}
}
private record RegistryResourceReloaderFactory(Identifier id, Function<RegistryWrapper.WrapperLookup, IdentifiableResourceReloadListener> listenerFactory) implements ListenerFactory {
private RegistryResourceReloaderFactory {
Objects.requireNonNull(listenerFactory);
}
@Override
public IdentifiableResourceReloadListener get(RegistryWrapper.WrapperLookup registry) {
final IdentifiableResourceReloadListener listener = listenerFactory.apply(registry);
if (!id.equals(listener.getFabricId())) {
throw new IllegalStateException("Listener factory for " + id + " created a listener with ID " + listener.getFabricId());
}
return listener;
}
}
}

View file

@ -4,3 +4,4 @@ accessible method net/minecraft/resource/NamespaceResourceManager getMetadataPat
accessible method net/minecraft/resource/NamespaceResourceManager loadMetadata (Lnet/minecraft/resource/InputSupplier;)Lnet/minecraft/resource/metadata/ResourceMetadata;
accessible field net/minecraft/resource/FileResourcePackProvider source Lnet/minecraft/resource/ResourcePackSource;
accessible field net/minecraft/resource/ResourcePackManager providers Ljava/util/Set;
accessible field net/minecraft/recipe/RecipeManager registryLookup Lnet/minecraft/registry/RegistryWrapper$WrapperLookup;

View file

@ -18,7 +18,11 @@ package net.fabricmc.fabric.test.resource.loader;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import net.minecraft.enchantment.Enchantments;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.RegistryWrapper;
import net.minecraft.resource.ResourceManager;
import net.minecraft.resource.ResourceType;
import net.minecraft.util.Identifier;
@ -116,5 +120,22 @@ public class ResourceReloadListenerTestMod implements ModInitializer {
serverResources = true;
}
});
ResourceManagerHelper.get(ResourceType.SERVER_DATA).registerReloadListener(RegistryReloader.ID, RegistryReloader::new);
}
private record RegistryReloader(RegistryWrapper.WrapperLookup wrapperLookup) implements SimpleSynchronousResourceReloadListener {
private static final Identifier ID = Identifier.of(MODID, "registry_reloader");
@Override
public Identifier getFabricId() {
return ID;
}
@Override
public void reload(ResourceManager manager) {
Objects.requireNonNull(wrapperLookup);
wrapperLookup.getWrapperOrThrow(RegistryKeys.ENCHANTMENT).getOrThrow(Enchantments.FORTUNE);
}
}
}